--- /tmp/dsg/dolibarr/htdocs/api/class/github_19.0.3_api.class.php +++ /tmp/dsg/dolibarr/htdocs/api/class/client_api.class.php @@ -4 +3,0 @@ - * Copyright (C) 2020 Frédéric France @@ -32,106 +31,72 @@ - /** - * @var DoliDb $db Database object - */ - protected $db; - - /** - * @var Restler $r Restler object - */ - public $r; - - /** - * Constructor - * - * @param DoliDb $db Database handler - * @param string $cachedir Cache dir - * @param boolean $refreshCache Update cache - */ - public function __construct($db, $cachedir = '', $refreshCache = false) - { - global $conf, $dolibarr_main_url_root; - - if (empty($cachedir)) { - $cachedir = $conf->api->dir_temp; - } - Defaults::$cacheDirectory = $cachedir; - - $this->db = $db; - $production_mode = (!getDolGlobalString('API_PRODUCTION_MODE') ? false : true); - $this->r = new Restler($production_mode, $refreshCache); - - $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root)); - $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file - - $urlwithouturlrootautodetect = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim(DOL_MAIN_URL_ROOT)); - $urlwithrootautodetect = $urlwithouturlroot.DOL_URL_ROOT; // This is to use local domain autodetected by dolibarr from url - - $this->r->setBaseUrls($urlwithouturlroot, $urlwithouturlrootautodetect); - $this->r->setAPIVersion(1); - //$this->r->setSupportedFormats('json'); - //$this->r->setSupportedFormats('jsonFormat'); - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore - /** - * Check and convert a string depending on its type/name. - * - * Display a short message an return a http code 200 - * - * @param string $field Field name - * @param mixed $value Value to check/clean - * @param Object $object Object - * @return string Value cleaned - */ - protected function _checkValForAPI($field, $value, $object) - { - // phpcs:enable - if (!is_array($value)) { - // TODO Use type detected in $object->fields if $object known and we can - if (in_array($field, array('note', 'note_private', 'note_public', 'desc', 'description'))) { - return sanitizeVal($value, 'restricthtml'); - } else { - return sanitizeVal($value, 'alphanohtml'); - } - } else { - // TODO Recall _checkValForAPI for each element of array - - return $value; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore - /** - * Filter properties that will be returned on object - * - * @param Object $object Object to clean - * @param String $properties Comma separated list of properties names - * @return Object Object with cleaned properties - */ - protected function _filterObjectProperties($object, $properties) - { - // If properties is empty, we return all properties - if (empty($properties)) { - return $object; - } - // Else we filter properties - foreach (get_object_vars($object) as $key => $value) { - if (!in_array($key, explode(',', $properties))) { - unset($object->$key); - } - } - return $object; - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore - /** - * Clean sensible object datas - * - * @param Object $object Object to clean - * @return Object Object with cleaned properties - */ - protected function _cleanObjectDatas($object) - { - // phpcs:enable - // Remove $db object property for object - unset($object->db); - unset($object->isextrafieldmanaged); + + /** + * @var DoliDb $db Database object + */ + protected static $db; + + /** + * @var Restler $r Restler object + */ + public $r; + + /** + * Constructor + * + * @param DoliDb $db Database handler + * @param string $cachedir Cache dir + * @param boolean $refreshCache Update cache + */ + public function __construct($db, $cachedir = '', $refreshCache = false) + { + global $conf, $dolibarr_main_url_root; + + if (empty($cachedir)) $cachedir = $conf->api->dir_temp; + Defaults::$cacheDirectory = $cachedir; + + $this->db = $db; + $production_mode = (empty($conf->global->API_PRODUCTION_MODE) ? false : true); + $this->r = new Restler($production_mode, $refreshCache); + + $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root)); + $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file + + $urlwithouturlrootautodetect = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim(DOL_MAIN_URL_ROOT)); + $urlwithrootautodetect = $urlwithouturlroot.DOL_URL_ROOT; // This is to use local domain autodetected by dolibarr from url + + $this->r->setBaseUrls($urlwithouturlroot, $urlwithouturlrootautodetect); + $this->r->setAPIVersion(1); + //$this->r->setSupportedFormats('json'); + //$this->r->setSupportedFormats('jsonFormat'); + } + + /** + * Executed method when API is called without parameter + * + * Display a short message an return a http code 200 + * + * @return array + */ + /* Disabled, most APIs does not share same signature for method index + function index() + { + return array( + 'success' => array( + 'code' => 200, + 'message' => __class__.' is up and running!' + ) + ); + }*/ + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore + /** + * Clean sensible object datas + * + * @param object $object Object to clean + * @return array Array of cleaned object properties + */ + protected function _cleanObjectDatas($object) + { + // phpcs:enable + // Remove $db object property for object + unset($object->db); + unset($object->isextrafieldmanaged); @@ -141,148 +106,121 @@ - unset($object->pass); - unset($object->pass_indatabase); - - // Remove linkedObjects. We should already have and keep only linkedObjectsIds that avoid huge responses - unset($object->linkedObjects); - //unset($object->lines[$i]->linked_objects); // This is the array to create linked object during create - - unset($object->fields); - unset($object->oldline); - - unset($object->error); - unset($object->errors); - unset($object->errorhidden); - - unset($object->ref_previous); - unset($object->ref_next); - unset($object->imgWidth); - unset($object->imgHeight); - unset($object->barcode_type_code); - unset($object->barcode_type_label); - - unset($object->mode_reglement); // We use mode_reglement_id now - unset($object->cond_reglement); // We use cond_reglement_id now - unset($object->note); // We use note_public or note_private now - unset($object->contact); // We use contact_id now - unset($object->thirdparty); // We use thirdparty_id or fk_soc or socid now - - unset($object->projet); // Should be fk_project - unset($object->project); // Should be fk_project - unset($object->fk_projet); // Should be fk_project - unset($object->author); // Should be fk_user_author - unset($object->timespent_old_duration); - unset($object->timespent_id); - unset($object->timespent_duration); - unset($object->timespent_date); - unset($object->timespent_datehour); - unset($object->timespent_withhour); - unset($object->timespent_fk_user); - unset($object->timespent_note); - unset($object->fk_delivery_address); - unset($object->modelpdf); - unset($object->sendtoid); - unset($object->name_bis); - unset($object->newref); - unset($object->alreadypaid); - unset($object->openid); - - //unset($object->labelStatus); - //unset($object->labelStatusShort); - - unset($object->stats_propale); - unset($object->stats_commande); - unset($object->stats_contrat); - unset($object->stats_facture); - unset($object->stats_commande_fournisseur); - unset($object->stats_reception); - unset($object->stats_mrptoconsume); - unset($object->stats_mrptoproduce); - - unset($object->element); - unset($object->element_for_permission); - unset($object->fk_element); - unset($object->table_element); - unset($object->table_element_line); - unset($object->class_element_line); - unset($object->picto); - - unset($object->fieldsforcombobox); - unset($object->regeximgext); - - unset($object->skip_update_total); - unset($object->context); - unset($object->next_prev_filter); - - unset($object->region); - unset($object->region_code); - unset($object->country); - unset($object->state); - unset($object->state_code); - unset($object->departement); - unset($object->departement_code); - - unset($object->libelle_statut); - unset($object->libelle_paiement); - - unset($object->prefix_comm); - - if (!isset($object->table_element) || $object->table_element != 'ticket') { - unset($object->comments); - } - - // Remove the $oldcopy property because it is not supported by the JSON - // encoder. The following error is generated when trying to serialize - // it: "Error encoding/decoding JSON: Type is not supported" - // Note: Event if this property was correctly handled by the JSON - // encoder, it should be ignored because keeping it would let the API - // have a very strange behavior: calling PUT and then GET on the same - // resource would give different results: - // PUT /objects/{id} -> returns object with oldcopy = previous version of the object - // GET /objects/{id} -> returns object with oldcopy empty - unset($object->oldcopy); - - // If object has lines, remove $db property - if (isset($object->lines) && is_array($object->lines) && count($object->lines) > 0) { - $nboflines = count($object->lines); - for ($i = 0; $i < $nboflines; $i++) { - $this->_cleanObjectDatas($object->lines[$i]); - - unset($object->lines[$i]->contact); - unset($object->lines[$i]->contact_id); - unset($object->lines[$i]->country); - unset($object->lines[$i]->country_id); - unset($object->lines[$i]->country_code); - unset($object->lines[$i]->mode_reglement_id); - unset($object->lines[$i]->mode_reglement_code); - unset($object->lines[$i]->mode_reglement); - unset($object->lines[$i]->cond_reglement_id); - unset($object->lines[$i]->cond_reglement_code); - unset($object->lines[$i]->cond_reglement); - unset($object->lines[$i]->fk_delivery_address); - unset($object->lines[$i]->fk_projet); - unset($object->lines[$i]->fk_project); - unset($object->lines[$i]->thirdparty); - unset($object->lines[$i]->user); - unset($object->lines[$i]->model_pdf); - unset($object->lines[$i]->modelpdf); - unset($object->lines[$i]->note_public); - unset($object->lines[$i]->note_private); - unset($object->lines[$i]->fk_incoterms); - unset($object->lines[$i]->label_incoterms); - unset($object->lines[$i]->location_incoterms); - unset($object->lines[$i]->name); - unset($object->lines[$i]->lastname); - unset($object->lines[$i]->firstname); - unset($object->lines[$i]->civility_id); - unset($object->lines[$i]->fk_multicurrency); - unset($object->lines[$i]->multicurrency_code); - unset($object->lines[$i]->shipping_method_id); - } - } - - if (!empty($object->thirdparty) && is_object($object->thirdparty)) { - $this->_cleanObjectDatas($object->thirdparty); - } - - if (!empty($object->product) && is_object($object->product)) { - $this->_cleanObjectDatas($object->product); - } + + // Remove linkedObjects. We should already have linkedObjectIds that avoid huge responses + unset($object->linkedObjects); + + unset($object->fields); + unset($object->oldline); + + unset($object->error); + unset($object->errors); + + unset($object->ref_previous); + unset($object->ref_next); + unset($object->ref_int); + + unset($object->projet); // Should be fk_project + unset($object->project); // Should be fk_project + unset($object->author); // Should be fk_user_author + unset($object->timespent_old_duration); + unset($object->timespent_id); + unset($object->timespent_duration); + unset($object->timespent_date); + unset($object->timespent_datehour); + unset($object->timespent_withhour); + unset($object->timespent_fk_user); + unset($object->timespent_note); + unset($object->fk_delivery_address); + + unset($object->statuts); + unset($object->statuts_short); + unset($object->statuts_logo); + unset($object->statuts_long); + unset($object->labelStatus); + unset($object->labelStatusShort); + + unset($object->stats_propale); + unset($object->stats_commande); + unset($object->stats_contrat); + unset($object->stats_facture); + unset($object->stats_commande_fournisseur); + unset($object->stats_reception); + unset($object->stats_mrptoconsume); + unset($object->stats_mrptoproduce); + + unset($object->element); + unset($object->fk_element); + unset($object->table_element); + unset($object->table_element_line); + unset($object->class_element_line); + unset($object->picto); + + unset($object->fieldsforcombobox); + + unset($object->skip_update_total); + unset($object->context); + unset($object->next_prev_filter); + + unset($object->region); + unset($object->region_code); + + unset($object->libelle_statut); + unset($object->libelle_paiement); + + if ($object->table_element != 'ticket') { + unset($object->comments); + } + + // Remove the $oldcopy property because it is not supported by the JSON + // encoder. The following error is generated when trying to serialize + // it: "Error encoding/decoding JSON: Type is not supported" + // Note: Event if this property was correctly handled by the JSON + // encoder, it should be ignored because keeping it would let the API + // have a very strange behavior: calling PUT and then GET on the same + // resource would give different results: + // PUT /objects/{id} -> returns object with oldcopy = previous version of the object + // GET /objects/{id} -> returns object with oldcopy empty + unset($object->oldcopy); + + // If object has lines, remove $db property + if (isset($object->lines) && is_array($object->lines) && count($object->lines) > 0) { + $nboflines = count($object->lines); + for ($i = 0; $i < $nboflines; $i++) + { + $this->_cleanObjectDatas($object->lines[$i]); + + unset($object->lines[$i]->contact); + unset($object->lines[$i]->contact_id); + unset($object->lines[$i]->country); + unset($object->lines[$i]->country_id); + unset($object->lines[$i]->country_code); + unset($object->lines[$i]->mode_reglement_id); + unset($object->lines[$i]->mode_reglement_code); + unset($object->lines[$i]->mode_reglement); + unset($object->lines[$i]->cond_reglement_id); + unset($object->lines[$i]->cond_reglement_code); + unset($object->lines[$i]->cond_reglement); + unset($object->lines[$i]->fk_delivery_address); + unset($object->lines[$i]->fk_projet); + unset($object->lines[$i]->fk_project); + unset($object->lines[$i]->thirdparty); + unset($object->lines[$i]->user); + unset($object->lines[$i]->model_pdf); + unset($object->lines[$i]->modelpdf); + unset($object->lines[$i]->note_public); + unset($object->lines[$i]->note_private); + unset($object->lines[$i]->fk_incoterms); + unset($object->lines[$i]->label_incoterms); + unset($object->lines[$i]->location_incoterms); + unset($object->lines[$i]->name); + unset($object->lines[$i]->lastname); + unset($object->lines[$i]->firstname); + unset($object->lines[$i]->civility_id); + unset($object->lines[$i]->fk_multicurrency); + unset($object->lines[$i]->multicurrency_code); + unset($object->lines[$i]->shipping_method_id); + } + } + + if (!empty($object->thirdparty) && is_object($object->thirdparty)) + { + $this->_cleanObjectDatas($object->thirdparty); + } @@ -291,3 +229,3 @@ - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore @@ -294,0 +233,2 @@ + * Check user access to a resource + * @@ -303 +243,2 @@ - * @return bool + * @return bool + * @throws RestException @@ -305,3 +246,3 @@ - protected static function _checkAccessToResource($resource, $resource_id = 0, $dbtablename = '', $feature2 = '', $dbt_keyfield = 'fk_soc', $dbt_select = 'rowid') - { - // phpcs:enable + protected static function _checkAccessToResource($resource, $resource_id = 0, $dbtablename = '', $feature2 = '', $dbt_keyfield = 'fk_soc', $dbt_select = 'rowid') + { + // phpcs:enable @@ -312 +253,2 @@ - } elseif (preg_match('/\|/', $resource)) { + } + elseif (preg_match('/\|/', $resource)) { @@ -322,3 +264,3 @@ - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore @@ -327 +268,0 @@ - * Function no more used. Kept for backward compatibility with old APIs of modules @@ -329,3 +270,2 @@ - * @param string $sqlfilters sqlfilter string - * @param string $error Error message - * @return boolean|string True if valid, False if not valid + * @param string $sqlfilters sqlfilter string + * @return boolean True if valid, False if not valid @@ -333 +273 @@ - protected function _checkFilters($sqlfilters, &$error = '') + protected function _checkFilters($sqlfilters) @@ -335,3 +275,20 @@ - // phpcs:enable - - return dolCheckFilters($sqlfilters, $error); + // phpcs:enable + //$regexstring='\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters); + $tmp = $sqlfilters; + $ok = 0; + $i = 0; $nb = strlen($tmp); + $counter = 0; + while ($i < $nb) + { + if ($tmp[$i] == '(') $counter++; + if ($tmp[$i] == ')') $counter--; + if ($counter < 0) + { + $error = "Bad sqlfilters=".$sqlfilters; + dol_syslog($error, LOG_WARNING); + return false; + } + $i++; + } + return true; @@ -340,2 +297,2 @@ - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore @@ -343,2 +300 @@ - * Function to forge a SQL criteria from a Generic filter string. - * Function no more used. Kept for backward compatibility with old APIs of modules + * Function to forge a SQL criteria @@ -346,4 +302,2 @@ - * @param array $matches Array of found string by regex search. - * Each entry is 1 and only 1 criteria. - * Example: "t.ref:like:'SO-%'", "t.date_creation:<:'20160101'", "t.date_creation:<:'2016-01-01 12:30:00'", "t.nature:is:NULL", "t.field2:isnot:NULL" - * @return string Forged criteria. Example: "t.field like 'abc%'" + * @param array $matches Array of found string by regex search. Example: "t.ref:like:'SO-%'" or "t.date_creation:<:'20160101'" or "t.nature:is:NULL" + * @return string Forged criteria. Example: "t.field like 'abc%'" @@ -353 +307,19 @@ - return dolForgeCriteriaCallback($matches); + // phpcs:enable + global $db; + + //dol_syslog("Convert matches ".$matches[1]); + if (empty($matches[1])) return ''; + $tmp = explode(':', $matches[1]); + if (count($tmp) < 3) return ''; + + $tmpescaped = $tmp[2]; + $regbis = array(); + if (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) + { + $tmpescaped = "'".$db->escape($regbis[1])."'"; + } + else + { + $tmpescaped = $db->escape($tmpescaped); + } + return $db->escape($tmp[0]).' '.strtoupper($db->escape($tmp[1]))." ".$tmpescaped; --- /tmp/dsg/dolibarr/htdocs/api/class/github_19.0.3_api_access.class.php +++ /tmp/dsg/dolibarr/htdocs/api/class/client_api_access.class.php @@ -4 +3,0 @@ - * Copyright (C) 2023 Ferran Marcet @@ -23,3 +22,3 @@ - $loader = Luracast\Restler\AutoLoader::instance(); - spl_autoload_register($loader); - return $loader; + $loader = Luracast\Restler\AutoLoader::instance(); + spl_autoload_register($loader); + return $loader; @@ -33,6 +32,6 @@ - -use Luracast\Restler\iAuthenticate; -use Luracast\Restler\iUseAuthentication; -use Luracast\Restler\Resources; -use Luracast\Restler\Defaults; -use Luracast\Restler\RestException; +use \Luracast\Restler\iAuthenticate; +use \Luracast\Restler\iUseAuthentication; +use \Luracast\Restler\Resources; +use \Luracast\Restler\Defaults; +use \Luracast\Restler\RestException; + @@ -49,5 +47,0 @@ - * @var DoliDB Database handler - */ - public $db; - - /** @@ -61 +55 @@ - public static $role = 'user'; + public static $role = 'user'; @@ -68,11 +62 @@ - - /** - * Constructor - */ - public function __construct() - { - global $db; - $this->db = $db; - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName + // phpcs:disable PEAR.NamingConventions.ValidFunctionName @@ -89,2 +73,2 @@ - // phpcs:enable - global $conf, $db, $user; + // phpcs:enable + global $conf, $db; @@ -99 +83 @@ - dol_syslog($key.' - '.$val); + dol_syslog($key.' - '.$val); @@ -104,16 +88,17 @@ - if (isset($_GET['api_key'])) { // For backward compatibility - // TODO Add option to disable use of api key on url. Return errors if used. - $api_key = $_GET['api_key']; - } - if (isset($_GET['DOLAPIKEY'])) { - // TODO Add option to disable use of api key on url. Return errors if used. - $api_key = $_GET['DOLAPIKEY']; // With GET method - } - if (isset($_SERVER['HTTP_DOLAPIKEY'])) { // Param DOLAPIKEY in header can be read with HTTP_DOLAPIKEY - $api_key = $_SERVER['HTTP_DOLAPIKEY']; // With header method (recommanded) - } - if (preg_match('/^dolcrypt:/i', $api_key)) { - throw new RestException(503, 'Bad value for the API key. An API key should not start with dolcrypt:'); - } - - if ($api_key) { + if (isset($_GET['api_key'])) // For backward compatibility + { + // TODO Add option to disable use of api key on url. Return errors if used. + $api_key = $_GET['api_key']; + } + if (isset($_GET['DOLAPIKEY'])) + { + // TODO Add option to disable use of api key on url. Return errors if used. + $api_key = $_GET['DOLAPIKEY']; // With GET method + } + if (isset($_SERVER['HTTP_DOLAPIKEY'])) // Param DOLAPIKEY in header can be read with HTTP_DOLAPIKEY + { + $api_key = $_SERVER['HTTP_DOLAPIKEY']; // With header method (recommanded) + } + + if ($api_key) + { @@ -122 +107 @@ - $sql = "SELECT u.login, u.datec, u.api_key,"; + $sql = "SELECT u.login, u.datec, u.api_key, "; @@ -125,7 +110,9 @@ - $sql .= " WHERE u.api_key = '".$this->db->escape($api_key)."' OR u.api_key = '".$this->db->escape(dolEncrypt($api_key, '', '', 'dolibarr'))."'"; - - $result = $this->db->query($sql); - if ($result) { - $nbrows = $this->db->num_rows($result); - if ($nbrows == 1) { - $obj = $this->db->fetch_object($result); + $sql .= " WHERE u.api_key = '".$db->escape($api_key)."'"; + // TODO Check if 2 users has same API key. + + $result = $db->query($sql); + if ($result) + { + if ($db->num_rows($result)) + { + $obj = $db->fetch_object($result); @@ -133 +120 @@ - $stored_key = dolDecrypt($obj->api_key); + $stored_key = $obj->api_key; @@ -136 +123,2 @@ - if (!defined("DOLENTITY") && $conf->entity != ($obj->entity ? $obj->entity : 1)) { // If API was not forced with HTTP_DOLENTITY, and user is on another entity, so we reset entity to entity of user + if (!defined("DOLENTITY") && $conf->entity != ($obj->entity ? $obj->entity : 1)) // If API was not forced with HTTP_DOLENTITY, and user is on another entity, so we reset entity to entity of user + { @@ -140 +128 @@ - $conf->setValues($this->db); + $conf->setValues($db); @@ -142,2 +129,0 @@ - } elseif ($nbrows > 1) { - throw new RestException(503, 'Error when fetching user api_key : More than 1 user with this apikey'); @@ -145,5 +131,6 @@ - } else { - throw new RestException(503, 'Error when fetching user api_key :'.$this->db->error_msg); - } - - if ($login && $stored_key != $api_key) { // This should not happen since we did a search on api_key + } + else { + throw new RestException(503, 'Error when fetching user api_key :'.$db->error_msg); + } + + if ($stored_key != $api_key) { // This should not happen since we did a search on api_key @@ -154,9 +141,5 @@ - $genericmessageerroruser = 'Error user not valid (not found with api key or bad status or bad validity dates) (conf->entity='.$conf->entity.')'; - - if (!$login) { - dol_syslog("functions_isallowed::check_user_api_key Authentication KO for api key: Error when searching login user from api key", LOG_NOTICE); - sleep(1); // Anti brut force protection. Must be same delay when user and password are not valid. - throw new RestException(401, $genericmessageerroruser); - } - - $fuser = new User($this->db); + if (!$login) + { + throw new RestException(503, 'Error when searching login user from api key'); + } + $fuser = new User($db); @@ -165,30 +148,2 @@ - dol_syslog("functions_isallowed::check_user_api_key Authentication KO for '".$login."': Failed to fetch on entity", LOG_NOTICE); - sleep(1); // Anti brut force protection. Must be same delay when user and password are not valid. - throw new RestException(401, $genericmessageerroruser); - } - - // Check if user status is enabled - if ($fuser->statut != $fuser::STATUS_ENABLED) { - // Status is disabled - dol_syslog("functions_isallowed::check_user_api_key Authentication KO for '".$login."': The user has been disabled", LOG_NOTICE); - sleep(1); // Anti brut force protection. Must be same delay when user and password are not valid. - throw new RestException(401, $genericmessageerroruser); - } - - // Check if session was unvalidated by a password change - if (($fuser->flagdelsessionsbefore && !empty($_SESSION["dol_logindate"]) && $fuser->flagdelsessionsbefore > $_SESSION["dol_logindate"])) { - // Session is no more valid - dol_syslog("functions_isallowed::check_user_api_key Authentication KO for '".$login."': The user has a date for session invalidation = ".$fuser->flagdelsessionsbefore." and a session date = ".$_SESSION["dol_logindate"].". We must invalidate its sessions."); - sleep(1); // Anti brut force protection. Must be same delay when user and password are not valid. - throw new RestException(401, $genericmessageerroruser); - } - - // Check date validity - if ($fuser->isNotIntoValidityDateRange()) { - // User validity dates are no more valid - dol_syslog("functions_isallowed::check_user_api_key Authentication KO for '".$login."': The user login has a validity between [".$fuser->datestartvalidity." and ".$fuser->dateendvalidity."], curren date is ".dol_now()); - sleep(1); // Anti brut force protection. Must be same delay when user and password are not valid. - throw new RestException(401, $genericmessageerroruser); - } - - // User seems valid + throw new RestException(503, 'Error when fetching user :'.$fuser->error.' (conf->entity='.$conf->entity.')'); + } @@ -196,2 +150,0 @@ - - // Set the property $user to the $user of API @@ -199,3 +151,0 @@ - - // Set also the global variable $user to the $user of API - $user = $fuser; @@ -205 +155 @@ - } + } @@ -209,12 +159,12 @@ - } - } else { - throw new RestException(401, "Failed to login to API. No parameter 'HTTP_DOLAPIKEY' on HTTP header (and no parameter DOLAPIKEY in URL)."); - } - - $userClass::setCacheIdentifier(static::$role); - Resources::$accessControlFunction = 'DolibarrApiAccess::verifyAccess'; - $requirefortest = static::$requires; - if (!is_array($requirefortest)) { - $requirefortest = explode(',', $requirefortest); - } - return in_array(static::$role, (array) $requirefortest) || static::$role == 'admin'; + } + } + else + { + throw new RestException(401, "Failed to login to API. No parameter 'HTTP_DOLAPIKEY' on HTTP header (and no parameter DOLAPIKEY in URL)."); + } + + $userClass::setCacheIdentifier(static::$role); + Resources::$accessControlFunction = 'DolibarrApiAccess::verifyAccess'; + $requirefortest = static::$requires; + if (!is_array($requirefortest)) $requirefortest = explode(',', $requirefortest); + return in_array(static::$role, (array) $requirefortest) || static::$role == 'admin'; @@ -223 +173 @@ - // phpcs:disable PEAR.NamingConventions.ValidFunctionName + // phpcs:disable PEAR.NamingConventions.ValidFunctionName @@ -227,25 +177,25 @@ - public function __getWWWAuthenticateString() - { - // phpcs:enable - return ''; - } - - /** - * Verify access - * - * @param array $m Properties of method - * - * @access private - * @return bool - */ - public static function verifyAccess(array $m) - { - $requires = isset($m['class']['DolibarrApiAccess']['properties']['requires']) - ? $m['class']['DolibarrApiAccess']['properties']['requires'] - : false; - - - return $requires - ? static::$role == 'admin' || in_array(static::$role, (array) $requires) - : true; - } + public function __getWWWAuthenticateString() + { + // phpcs:enable + return ''; + } + + /** + * Verify access + * + * @param array $m Properties of method + * + * @access private + * @return bool + */ + public static function verifyAccess(array $m) + { + $requires = isset($m['class']['DolibarrApiAccess']['properties']['requires']) + ? $m['class']['DolibarrApiAccess']['properties']['requires'] + : false; + + + return $requires + ? static::$role == 'admin' || in_array(static::$role, (array) $requires) + : true; + } --- /tmp/dsg/dolibarr/htdocs/api/class/github_19.0.3_api_documents.class.php +++ /tmp/dsg/dolibarr/htdocs/api/class/client_api_documents.class.php @@ -5 +4,0 @@ - * Copyright (C) 2023 Romain Neil @@ -23,0 +23 @@ + @@ -25 +24,0 @@ -require_once DOL_DOCUMENT_ROOT.'/api/class/api.class.php'; @@ -35,0 +35 @@ + @@ -39 +39 @@ - public static $DOCUMENT_FIELDS = array( + static $DOCUMENT_FIELDS = array( @@ -71 +71 @@ - global $conf; + global $conf, $langs; @@ -74 +74 @@ - throw new RestException(400, 'bad value for parameter modulepart'); + throw new RestException(400, 'bad value for parameter modulepart'); @@ -109 +109,2 @@ - if (!file_exists($original_file_osencoded)) { + if (!file_exists($original_file_osencoded)) + { @@ -122,5 +123,3 @@ - * Test sample 1: { "modulepart": "invoice", "original_file": "FA1701-001/FA1701-001.pdf", "doctemplate": "crabe", "langcode": "fr_FR" }. - * - * Supported modules: invoice, order, proposal, contract, shipment - * - * @param string $modulepart Name of module or area concerned by file download ('thirdparty', 'member', 'proposal', 'supplier_proposal', 'order', 'supplier_order', 'invoice', 'supplier_invoice', 'shipment', 'project', ...) + * Test sample 1: { "module_part": "invoice", "original_file": "FA1701-001/FA1701-001.pdf", "doctemplate": "crabe", "langcode": "fr_FR" }. + * + * @param string $modulepart Name of module or area concerned by file download ('invoice', 'order', ...). @@ -132 +131 @@ - * @throws RestException 500 System error + * @throws RestException 500 @@ -152 +151,2 @@ - if ($langcode && $langs->defaultlang != $langcode) { + if ($langcode && $langs->defaultlang != $langcode) + { @@ -184,3 +184,3 @@ - $hidedetails = !getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 0 : 1; - $hidedesc = !getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 0 : 1; - $hideref = !getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 0 : 1; + $hidedetails = empty($conf->global->MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS) ? 0 : 1; + $hidedesc = empty($conf->global->MAIN_GENERATE_DOCUMENTS_HIDE_DESC) ? 0 : 1; + $hideref = empty($conf->global->MAIN_GENERATE_DOCUMENTS_HIDE_REF) ? 0 : 1; @@ -190 +190,2 @@ - if ($modulepart == 'facture' || $modulepart == 'invoice') { + if ($modulepart == 'facture' || $modulepart == 'invoice') + { @@ -192,2 +193,2 @@ - $tmpobject = new Facture($this->db); - $result = $tmpobject->fetch(0, preg_replace('/\.[^\.]+$/', '', basename($original_file))); + $this->invoice = new Facture($this->db); + $result = $this->invoice->fetch(0, preg_replace('/\.[^\.]+$/', '', basename($original_file))); @@ -198,2 +199,2 @@ - $templateused = $doctemplate ? $doctemplate : $tmpobject->model_pdf; - $result = $tmpobject->generateDocument($templateused, $outputlangs, $hidedetails, $hidedesc, $hideref); + $templateused = $doctemplate ? $doctemplate : $this->invoice->modelpdf; + $result = $this->invoice->generateDocument($templateused, $outputlangs, $hidedetails, $hidedesc, $hideref); @@ -203,14 +204,3 @@ - } elseif ($modulepart == 'facture_fournisseur' || $modulepart == 'invoice_supplier') { - require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.facture.class.php'; - $tmpobject = new FactureFournisseur($this->db); - $result = $tmpobject->fetch(0, preg_replace('/\.[^\.]+$/', '', basename($original_file))); - if (!$result) { - throw new RestException(404, 'Supplier invoice not found'); - } - - $templateused = $doctemplate ? $doctemplate : $tmpobject->model_pdf; - $result = $tmpobject->generateDocument($templateused, $outputlangs, $hidedetails, $hidedesc, $hideref); - if ($result < 0) { - throw new RestException(500, 'Error generating document'); - } - } elseif ($modulepart == 'commande' || $modulepart == 'order') { + } + elseif ($modulepart == 'commande' || $modulepart == 'order') + { @@ -218,2 +208,2 @@ - $tmpobject = new Commande($this->db); - $result = $tmpobject->fetch(0, preg_replace('/\.[^\.]+$/', '', basename($original_file))); + $this->order = new Commande($this->db); + $result = $this->order->fetch(0, preg_replace('/\.[^\.]+$/', '', basename($original_file))); @@ -223,2 +213,2 @@ - $templateused = $doctemplate ? $doctemplate : $tmpobject->model_pdf; - $result = $tmpobject->generateDocument($templateused, $outputlangs, $hidedetails, $hidedesc, $hideref); + $templateused = $doctemplate ? $doctemplate : $this->order->modelpdf; + $result = $this->order->generateDocument($templateused, $outputlangs, $hidedetails, $hidedesc, $hideref); @@ -228 +218,3 @@ - } elseif ($modulepart == 'propal' || $modulepart == 'proposal') { + } + elseif ($modulepart == 'propal' || $modulepart == 'proposal') + { @@ -230,2 +222,2 @@ - $tmpobject = new Propal($this->db); - $result = $tmpobject->fetch(0, preg_replace('/\.[^\.]+$/', '', basename($original_file))); + $this->propal = new Propal($this->db); + $result = $this->propal->fetch(0, preg_replace('/\.[^\.]+$/', '', basename($original_file))); @@ -235,2 +227,2 @@ - $templateused = $doctemplate ? $doctemplate : $tmpobject->model_pdf; - $result = $tmpobject->generateDocument($templateused, $outputlangs, $hidedetails, $hidedesc, $hideref); + $templateused = $doctemplate ? $doctemplate : $this->propal->modelpdf; + $result = $this->propal->generateDocument($templateused, $outputlangs, $hidedetails, $hidedesc, $hideref); @@ -240,33 +232,3 @@ - } elseif ($modulepart == 'contrat' || $modulepart == 'contract') { - require_once DOL_DOCUMENT_ROOT . '/contrat/class/contrat.class.php'; - - $tmpobject = new Contrat($this->db); - $result = $tmpobject->fetch(0, preg_replace('/\.[^\.]+$/', '', basename($original_file))); - - if (!$result) { - throw new RestException(404, 'Contract not found'); - } - - $templateused = $doctemplate ? $doctemplate : $tmpobject->model_pdf; - $result = $tmpobject->generateDocument($templateused, $outputlangs, $hidedetails, $hidedesc, $hideref); - - if ($result <= 0) { - throw new RestException(500, 'Error generating document missing doctemplate parameter'); - } - } elseif ($modulepart == 'expedition' || $modulepart == 'shipment') { - require_once DOL_DOCUMENT_ROOT . '/expedition/class/expedition.class.php'; - - $tmpobject = new Expedition($this->db); - $result = $tmpobject->fetch(0, preg_replace('/\.[^\.]+$/', '', basename($original_file))); - - if (!$result) { - throw new RestException(404, 'Shipment not found'); - } - - $templateused = $doctemplate ? $doctemplate : $tmpobject->model_pdf; - $result = $tmpobject->generateDocument($templateused, $outputlangs, $hidedetails, $hidedesc, $hideref); - - if ($result <= 0) { - throw new RestException(500, 'Error generating document missing doctemplate parameter'); - } - } else { + } + else + { @@ -279 +241,2 @@ - if (!file_exists($original_file_osencoded)) { + if (!file_exists($original_file_osencoded)) + { @@ -289,2 +251,0 @@ - * - * Supported modules: thirdparty, user, member, proposal, order, supplier_order, shipment, invoice, supplier_invoice, product, event, expensereport, knowledgemanagement, category, contract @@ -302 +263 @@ - * @throws RestException 500 System error + * @throws RestException 500 @@ -319,4 +280,3 @@ - $recursive = 0; - $type = 'files'; - - if ($modulepart == 'societe' || $modulepart == 'thirdparty') { + + if ($modulepart == 'societe' || $modulepart == 'thirdparty') + { @@ -325 +285 @@ - if (!DolibarrApiAccess::$user->hasRight('societe', 'lire')) { + if (!DolibarrApiAccess::$user->rights->societe->lire) { @@ -336 +296,3 @@ - } elseif ($modulepart == 'user') { + } + elseif ($modulepart == 'user') + { @@ -351 +313,3 @@ - } elseif ($modulepart == 'adherent' || $modulepart == 'member') { + } + elseif ($modulepart == 'adherent' || $modulepart == 'member') + { @@ -365 +329,3 @@ - } elseif ($modulepart == 'propal' || $modulepart == 'proposal') { + } + elseif ($modulepart == 'propal' || $modulepart == 'proposal') + { @@ -368 +334 @@ - if (!DolibarrApiAccess::$user->hasRight('propal', 'lire')) { + if (!DolibarrApiAccess::$user->rights->propal->lire) { @@ -379,15 +345,3 @@ - } elseif ($modulepart == 'supplier_proposal') { - require_once DOL_DOCUMENT_ROOT.'/supplier_proposal/class/supplier_proposal.class.php'; - - if (!DolibarrApiAccess::$user->rights->supplier_proposal->read) { - throw new RestException(401); - } - - $object = new Propal($this->db); - $result = $object->fetch($id, $ref); - if (!$result) { - throw new RestException(404, 'Supplier proposal not found'); - } - - $upload_dir = $conf->propal->multidir_output[$object->entity]."/".get_exdir(0, 0, 0, 1, $object, 'propal'); - } elseif ($modulepart == 'commande' || $modulepart == 'order') { + } + elseif ($modulepart == 'commande' || $modulepart == 'order') + { @@ -396 +350 @@ - if (!DolibarrApiAccess::$user->hasRight('commande', 'lire')) { + if (!DolibarrApiAccess::$user->rights->commande->lire) { @@ -407,17 +361,3 @@ - } elseif ($modulepart == 'commande_fournisseur' || $modulepart == 'supplier_order') { - $modulepart = 'supplier_order'; - - require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php'; - - if (empty(DolibarrApiAccess::$user->rights->fournisseur->commande->lire) && empty(DolibarrApiAccess::$user->rights->supplier_order->lire)) { - throw new RestException(401); - } - - $object = new CommandeFournisseur($this->db); - $result = $object->fetch($id, $ref); - if (!$result) { - throw new RestException(404, 'Purchase order not found'); - } - - $upload_dir = $conf->fournisseur->dir_output."/commande/".dol_sanitizeFileName($object->ref); - } elseif ($modulepart == 'shipment' || $modulepart == 'expedition') { + } + elseif ($modulepart == 'shipment' || $modulepart == 'expedition') + { @@ -437 +377,3 @@ - } elseif ($modulepart == 'facture' || $modulepart == 'invoice') { + } + elseif ($modulepart == 'facture' || $modulepart == 'invoice') + { @@ -440 +382 @@ - if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) { + if (!DolibarrApiAccess::$user->rights->facture->lire) { @@ -451 +393,3 @@ - } elseif ($modulepart == 'facture_fournisseur' || $modulepart == 'supplier_invoice') { + } + elseif ($modulepart == 'facture_fournisseur' || $modulepart == 'supplier_invoice') + { @@ -456 +400 @@ - if (empty(DolibarrApiAccess::$user->rights->fournisseur->facture->lire) && empty(DolibarrApiAccess::$user->rights->supplier_invoice->lire)) { + if (!DolibarrApiAccess::$user->rights->fournisseur->facture->lire) { @@ -467 +411,3 @@ - } elseif ($modulepart == 'produit' || $modulepart == 'product') { + } + elseif ($modulepart == 'produit' || $modulepart == 'product') + { @@ -476 +422 @@ - if ($result == 0) { + if (!$result) { @@ -478,6 +424,6 @@ - } elseif ($result < 0) { - throw new RestException(500, 'Error while fetching object: '.$object->error); - } - - $upload_dir = $conf->product->multidir_output[$object->entity].'/'.get_exdir(0, 0, 0, 1, $object, 'product'); - } elseif ($modulepart == 'agenda' || $modulepart == 'action' || $modulepart == 'event') { + } + + $upload_dir = $conf->product->multidir_output[$object->entity].'/'.get_exdir(0, 0, 0, 0, $object, 'product').dol_sanitizeFileName($object->ref); + } + elseif ($modulepart == 'agenda' || $modulepart == 'action' || $modulepart == 'event') + { @@ -497 +443,3 @@ - } elseif ($modulepart == 'expensereport') { + } + elseif ($modulepart == 'expensereport') + { @@ -511,15 +459,3 @@ - } elseif ($modulepart == 'knowledgemanagement') { - require_once DOL_DOCUMENT_ROOT.'/knowledgemanagement/class/knowledgerecord.class.php'; - - if (!DolibarrApiAccess::$user->hasRight('knowledgemanagement', 'knowledgerecord', 'read') && !DolibarrApiAccess::$user->hasRight('knowledgemanagement', 'knowledgerecord', 'read')) { - throw new RestException(401); - } - - $object = new KnowledgeRecord($this->db); - $result = $object->fetch($id, $ref); - if (!$result) { - throw new RestException(404, 'KM article not found'); - } - - $upload_dir = $conf->knowledgemanagement->dir_output.'/knowledgerecord/'.dol_sanitizeFileName($object->ref); - } elseif ($modulepart == 'categorie' || $modulepart == 'category') { + } + elseif ($modulepart == 'categorie' || $modulepart == 'category') + { @@ -539,39 +475,3 @@ - } elseif ($modulepart == 'ecm') { - throw new RestException(500, 'Modulepart Ecm not implemented yet.'); - // require_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmdirectory.class.php'; - - // if (!DolibarrApiAccess::$user->rights->ecm->read) { - // throw new RestException(401); - // } - - // // $object = new EcmDirectory($this->db); - // // $result = $object->fetch($ref); - // // if (!$result) { - // // throw new RestException(404, 'EcmDirectory not found'); - // // } - // $upload_dir = $conf->ecm->dir_output; - // $type = 'all'; - // $recursive = 0; - } elseif ($modulepart == 'contrat' || $modulepart == 'contract') { - $modulepart = 'contrat'; - require_once DOL_DOCUMENT_ROOT . '/contrat/class/contrat.class.php'; - - $object = new Contrat($this->db); - $result = $object->fetch($id, $ref); - if (!$result) { - throw new RestException(404, 'Contract not found'); - } - - $upload_dir = $conf->contrat->dir_output . "/" . get_exdir(0, 0, 0, 1, $object, 'contract'); - } elseif ($modulepart == 'projet' || $modulepart == 'project') { - $modulepart = 'project'; - require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php'; - - $object = new Project($this->db); - $result = $object->fetch($id, $ref); - if (!$result) { - throw new RestException(404, 'Project not found'); - } - - $upload_dir = $conf->projet->dir_output . "/" . get_exdir(0, 0, 0, 1, $object, 'project'); - } else { + } + else + { @@ -581,6 +481 @@ - $objectType = $modulepart; - if (! empty($object->id) && ! empty($object->table_element)) { - $objectType = $object->table_element; - } - - $filearray = dol_dir_list($upload_dir, $type, $recursive, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder) == 'desc' ? SORT_DESC : SORT_ASC), 1); + $filearray = dol_dir_list($upload_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder) == 'desc' ?SORT_DESC:SORT_ASC), 1); @@ -588,19 +483 @@ - throw new RestException(404, 'Search for modulepart '.$modulepart.' with Id '.$object->id.(!empty($object->ref) ? ' or Ref '.$object->ref : '').' does not return any document.'); - } else { - if (($object->id) > 0 && !empty($modulepart)) { - require_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php'; - $ecmfile = new EcmFiles($this->db); - $result = $ecmfile->fetchAll('', '', 0, 0, array('t.src_object_type' => $objectType, 't.src_object_id' => $object->id)); - if ($result < 0) { - throw new RestException(503, 'Error when retrieve ecm list : '.$this->db->lasterror()); - } elseif (is_array($ecmfile->lines) && count($ecmfile->lines) > 0) { - $count = count($filearray); - for ($i = 0 ; $i < $count ; $i++) { - foreach ($ecmfile->lines as $line) { - if ($filearray[$i]['name'] == $line->filename) { - $filearray[$i] = array_merge($filearray[$i], (array) $line); - } - } - } - } - } + throw new RestException(404, 'Search for modulepart '.$modulepart.' with Id '.$object->id.(!empty($object->Ref) ? ' or Ref '.$object->ref : '').' does not return any document.'); @@ -622,3 +499,3 @@ - public function get($id) { - return array('note'=>'xxx'); - }*/ + public function get($id) { + return array('note'=>'xxx'); + }*/ @@ -628 +505 @@ - * Upload a document. + * Upload a file. @@ -634,11 +511,8 @@ - * Supported modules: invoice, order, supplier_order, task/project_task, product/service, expensereport, fichinter, member, propale, agenda, contact - * - * @param string $filename Name of file to create ('FA1705-0123.txt') - * @param string $modulepart Name of module or area concerned by file upload ('product', 'service', 'invoice', 'proposal', 'project', 'project_task', 'supplier_invoice', 'expensereport', 'member', ...) - * @param string $ref Reference of object (This will define subdir automatically and store submited file into it) - * @param string $subdir Subdirectory (Only if ref not provided) - * @param string $filecontent File content (string with file content. An empty file will be created if this parameter is not provided) - * @param string $fileencoding File encoding (''=no encoding, 'base64'=Base 64) - * @param int $overwriteifexists Overwrite file if exists (1 by default) - * @param int $createdirifnotexists Create subdirectories if the doesn't exists (1 by default) - * @return string + * @param string $filename Name of file to create ('FA1705-0123.txt') + * @param string $modulepart Name of module or area concerned by file upload ('facture', 'project', 'project_task', ...) + * @param string $ref Reference of object (This will define subdir automatically and store submited file into it) + * @param string $subdir Subdirectory (Only if ref not provided) + * @param string $filecontent File content (string with file content. An empty file will be created if this parameter is not provided) + * @param string $fileencoding File encoding (''=no encoding, 'base64'=Base 64) + * @param int $overwriteifexists Overwrite file if exists (1 by default) + * @return string @@ -649 +523 @@ - * @throws RestException 500 System error + * @throws RestException 500 @@ -653 +527 @@ - public function post($filename, $modulepart, $ref = '', $subdir = '', $filecontent = '', $fileencoding = '', $overwriteifexists = 0, $createdirifnotexists = 1) + public function post($filename, $modulepart, $ref = '', $subdir = '', $filecontent = '', $fileencoding = '', $overwriteifexists = 0) @@ -655,9 +529,9 @@ - global $conf; - - //var_dump($modulepart); - //var_dump($filename); - //var_dump($filecontent);exit; - - $modulepartorig = $modulepart; - - if (empty($modulepart)) { + global $db, $conf; + + /*var_dump($modulepart); + var_dump($filename); + var_dump($filecontent); + exit;*/ + + if (empty($modulepart)) + { @@ -666,0 +541,4 @@ + if (!DolibarrApiAccess::$user->rights->ecm->upload) { + throw new RestException(401); + } + @@ -668,6 +546,2 @@ - if (empty($fileencoding)) { - $newfilecontent = $filecontent; - } - if ($fileencoding == 'base64') { - $newfilecontent = base64_decode($filecontent); - } + if (empty($fileencoding)) $newfilecontent = $filecontent; + if ($fileencoding == 'base64') $newfilecontent = base64_decode($filecontent); @@ -680,5 +554,2 @@ - if (empty($entity)) { - $entity = 1; - } - - if ($ref) { + if ($ref) + { @@ -686,3 +557,3 @@ - $fetchbyid = false; - - if ($modulepart == 'facture' || $modulepart == 'invoice') { + + if ($modulepart == 'facture' || $modulepart == 'invoice') + { @@ -693 +564,3 @@ - } elseif ($modulepart == 'facture_fournisseur' || $modulepart == 'supplier_invoice') { + } + elseif ($modulepart == 'facture_fournisseur' || $modulepart == 'supplier_invoice') + { @@ -698,11 +571,3 @@ - } elseif ($modulepart == 'commande' || $modulepart == 'order') { - $modulepart = 'commande'; - - require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php'; - $object = new Commande($this->db); - } elseif ($modulepart == 'commande_fournisseur' || $modulepart == 'supplier_order') { - $modulepart = 'supplier_order'; - - require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php'; - $object = new CommandeFournisseur($this->db); - } elseif ($modulepart == 'projet' || $modulepart == 'project') { + } + elseif ($modulepart == 'project') + { @@ -711 +576,3 @@ - } elseif ($modulepart == 'task' || $modulepart == 'project_task') { + } + elseif ($modulepart == 'task' || $modulepart == 'project_task') + { @@ -720 +587,2 @@ - if ($task_result > 0) { + if ($task_result > 0) + { @@ -723 +591,2 @@ - if ($project_result >= 0) { + if ($project_result >= 0) + { @@ -726 +595,3 @@ - } else { + } + else + { @@ -729 +600,3 @@ - } elseif ($modulepart == 'product' || $modulepart == 'produit' || $modulepart == 'service' || $modulepart == 'produit|service') { + } + elseif ($modulepart == 'product' || $modulepart == 'produit' || $modulepart == 'service' || $modulepart == 'produit|service') + { @@ -732 +605,3 @@ - } elseif ($modulepart == 'expensereport') { + } + elseif ($modulepart == 'expensereport') + { @@ -735,4 +610,3 @@ - } elseif ($modulepart == 'fichinter') { - require_once DOL_DOCUMENT_ROOT.'/fichinter/class/fichinter.class.php'; - $object = new Fichinter($this->db); - } elseif ($modulepart == 'adherent' || $modulepart == 'member') { + } + elseif ($modulepart == 'adherent' || $modulepart == 'member') + { @@ -742,19 +616,4 @@ - } elseif ($modulepart == 'proposal' || $modulepart == 'propal' || $modulepart == 'propale') { - $modulepart = 'propale'; - require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php'; - $object = new Propal($this->db); - } elseif ($modulepart == 'agenda' || $modulepart == 'action' || $modulepart == 'event') { - $modulepart = 'agenda'; - require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php'; - $object = new ActionComm($this->db); - } elseif ($modulepart == 'contact' || $modulepart == 'socpeople') { - $modulepart = 'contact'; - require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php'; - $object = new Contact($this->db); - $fetchbyid = true; - } elseif ($modulepart == 'contrat' || $modulepart == 'contract') { - $modulepart = 'contrat'; - require_once DOL_DOCUMENT_ROOT . '/contrat/class/contrat.class.php'; - $object = new Contrat($this->db); - } else { - // TODO Implement additional moduleparts + } + // TODO Implement additional moduleparts + else + { @@ -764,5 +623,11 @@ - if (is_object($object)) { - if ($fetchbyid) { - $result = $object->fetch($ref); - } else { - $result = $object->fetch('', $ref); + if (is_object($object)) + { + $result = $object->fetch('', $ref); + + if ($result == 0) + { + throw new RestException(404, "Object with ref '".$ref."' was not found."); + } + elseif ($result < 0) + { + throw new RestException(500, 'Error while fetching object.'); @@ -770,10 +635,5 @@ - - if ($result == 0) { - throw new RestException(404, "Object with ref '".$ref."' was not found."); - } elseif ($result < 0) { - throw new RestException(500, 'Error while fetching object: '.$object->error); - } - } - - if (!($object->id > 0)) { - throw new RestException(404, 'The object '.$modulepart." with ref '".$ref."' was not found."); + } + + if (!($object->id > 0)) + { + throw new RestException(404, 'The object '.$modulepart." with ref '".$ref."' was not found."); @@ -783 +643 @@ - // In future, all object should use this to define path of documents. + // If future, all object should use this to define path of documents. @@ -788,41 +648,23 @@ - // Test on permissions - if ($modulepart != 'ecm') { - $relativefile = $tmpreldir.dol_sanitizeFileName($object->ref); - $tmp = dol_check_secure_access_document($modulepart, $relativefile, $entity, DolibarrApiAccess::$user, $ref, 'write'); - $upload_dir = $tmp['original_file']; // No dirname here, tmp['original_file'] is already the dir because dol_check_secure_access_document was called with param original_file that is only the dir - } else { - if (!DolibarrApiAccess::$user->hasRight('ecm', 'upload')) { - throw new RestException(401, 'Missing permission to upload files in ECM module'); - } - $upload_dir = $conf->medias->multidir_output[$conf->entity]; - } - - if (empty($upload_dir) || $upload_dir == '/') { - throw new RestException(500, 'This value of modulepart ('.$modulepart.') does not support yet usage of ref. Check modulepart parameter or try to use subdir parameter instead of ref.'); - } - } else { - if ($modulepart == 'invoice') { - $modulepart = 'facture'; - } - if ($modulepart == 'member') { - $modulepart = 'adherent'; - } - - // Test on permissions - if ($modulepart != 'ecm') { - $relativefile = $subdir; - $tmp = dol_check_secure_access_document($modulepart, $relativefile, $entity, DolibarrApiAccess::$user, '', 'write'); - $upload_dir = $tmp['original_file']; // No dirname here, tmp['original_file'] is already the dir because dol_check_secure_access_document was called with param original_file that is only the dir - } else { - if (!DolibarrApiAccess::$user->hasRight('ecm', 'upload')) { - throw new RestException(401, 'Missing permission to upload files in ECM module'); - } - $upload_dir = $conf->medias->multidir_output[$conf->entity]; - } - - if (empty($upload_dir) || $upload_dir == '/') { - if (!empty($tmp['error'])) { - throw new RestException(401, 'Error returned by dol_check_secure_access_document: '.$tmp['error']); - } else { - throw new RestException(500, 'This value of modulepart ('.$modulepart.') is not allowed with this value of subdir ('.$relativefile.')'); - } + $relativefile = $tmpreldir.dol_sanitizeFileName($object->ref); + + $tmp = dol_check_secure_access_document($modulepart, $relativefile, $entity, DolibarrApiAccess::$user, $ref, 'write'); + $upload_dir = $tmp['original_file']; // No dirname here, tmp['original_file'] is already the dir because dol_check_secure_access_document was called with param original_file that is only the dir + + if (empty($upload_dir) || $upload_dir == '/') + { + throw new RestException(500, 'This value of modulepart does not support yet usage of ref. Check modulepart parameter or try to use subdir parameter instead of ref.'); + } + } + else + { + if ($modulepart == 'invoice') $modulepart = 'facture'; + if ($modulepart == 'member') $modulepart = 'adherent'; + + $relativefile = $subdir; + + $tmp = dol_check_secure_access_document($modulepart, $relativefile, $entity, DolibarrApiAccess::$user, '', 'write'); + $upload_dir = $tmp['original_file']; // No dirname here, tmp['original_file'] is already the dir because dol_check_secure_access_document was called with param original_file that is only the dir + + if (empty($upload_dir) || $upload_dir == '/') + { + throw new RestException(500, 'This value of modulepart does not support yet usage of ref. Check modulepart parameter or try to use subdir parameter instead of ref.'); @@ -835,4 +677,3 @@ - if (!empty($createdirifnotexists)) { - if (dol_mkdir($upload_dir) < 0) { // needed by products - throw new RestException(500, 'Error while trying to create directory '.$upload_dir); - } + if (dol_mkdir($upload_dir) < 0) // needed by products + { + throw new RestException(500, 'Error while trying to create directory.'); @@ -850 +691,2 @@ - if (!$overwriteifexists && dol_is_file($destfile)) { + if (!$overwriteifexists && dol_is_file($destfile)) + { @@ -854,5 +695,0 @@ - // in case temporary directory admin/temp doesn't exist - if (!dol_is_dir(dirname($destfiletmp))) { - dol_mkdir(dirname($destfiletmp)); - } - @@ -860 +697,2 @@ - if ($fhandle) { + if ($fhandle) + { @@ -863,2 +701,4 @@ - dolChmod($destfiletmp); - } else { + @chmod($destfiletmp, octdec($conf->global->MAIN_UMASK)); + } + else + { @@ -868,52 +708,3 @@ - $disablevirusscan = 0; - $src_file = $destfiletmp; - $dest_file = $destfile; - - // Security: - // If we need to make a virus scan - if (empty($disablevirusscan) && file_exists($src_file)) { - $checkvirusarray = dolCheckVirus($src_file); - if (count($checkvirusarray)) { - dol_syslog('Files.lib::dol_move_uploaded_file File "'.$src_file.'" (target name "'.$dest_file.'") KO with antivirus: errors='.join(',', $checkvirusarray), LOG_WARNING); - throw new RestException(500, 'ErrorFileIsInfectedWithAVirus: '.join(',', $checkvirusarray)); - } - } - - // Security: - // Disallow file with some extensions. We rename them. - // Because if we put the documents directory into a directory inside web root (very bad), this allows to execute on demand arbitrary code. - if (isAFileWithExecutableContent($dest_file) && !getDolGlobalString('MAIN_DOCUMENT_IS_OUTSIDE_WEBROOT_SO_NOEXE_NOT_REQUIRED')) { - // $upload_dir ends with a slash, so be must be sure the medias dir to compare to ends with slash too. - $publicmediasdirwithslash = $conf->medias->multidir_output[$conf->entity]; - if (!preg_match('/\/$/', $publicmediasdirwithslash)) { - $publicmediasdirwithslash .= '/'; - } - - if (strpos($upload_dir, $publicmediasdirwithslash) !== 0 || !getDolGlobalInt("MAIN_DOCUMENT_DISABLE_NOEXE_IN_MEDIAS_DIR")) { // We never add .noexe on files into media directory - $dest_file .= '.noexe'; - } - } - - // Security: - // We refuse cache files/dirs, upload using .. and pipes into filenames. - if (preg_match('/^\./', basename($src_file)) || preg_match('/\.\./', $src_file) || preg_match('/[<>|]/', $src_file)) { - dol_syslog("Refused to deliver file ".$src_file, LOG_WARNING); - throw new RestException(500, "Refused to deliver file ".$src_file); - } - - // Security: - // We refuse cache files/dirs, upload using .. and pipes into filenames. - if (preg_match('/^\./', basename($dest_file)) || preg_match('/\.\./', $dest_file) || preg_match('/[<>|]/', $dest_file)) { - dol_syslog("Refused to deliver file ".$dest_file, LOG_WARNING); - throw new RestException(500, "Refused to deliver file ".$dest_file); - } - - $moreinfo = array('note_private' => 'File uploaded using API /documents from IP '.getUserRemoteIP()); - if (!empty($object) && is_object($object) && $object->id > 0) { - $moreinfo['src_object_type'] = $object->table_element; - $moreinfo['src_object_id'] = $object->id; - } - - // Move the temporary file at its final emplacement - $result = dol_move($destfiletmp, $dest_file, 0, $overwriteifexists, 1, 1, $moreinfo); - if (!$result) { + $result = dol_move($destfiletmp, $destfile, 0, $overwriteifexists, 1); + if (!$result) + { @@ -941,53 +732,54 @@ - global $conf, $langs; - - if (empty($modulepart)) { - throw new RestException(400, 'bad value for parameter modulepart'); - } - if (empty($original_file)) { - throw new RestException(400, 'bad value for parameter original_file'); - } - - //--- Finds and returns the document - $entity = $conf->entity; - - // Special cases that need to use get_exdir to get real dir of object - // If future, all object should use this to define path of documents. - /* - $tmpreldir = ''; - if ($modulepart == 'supplier_invoice') { - $tmpreldir = get_exdir($object->id, 2, 0, 0, $object, 'invoice_supplier'); - } - - $relativefile = $tmpreldir.dol_sanitizeFileName($object->ref); */ - $relativefile = $original_file; - - $check_access = dol_check_secure_access_document($modulepart, $relativefile, $entity, DolibarrApiAccess::$user, '', 'read'); - $accessallowed = $check_access['accessallowed']; - $sqlprotectagainstexternals = $check_access['sqlprotectagainstexternals']; - $original_file = $check_access['original_file']; - - if (preg_match('/\.\./', $original_file) || preg_match('/[<>|]/', $original_file)) { - throw new RestException(401); - } - if (!$accessallowed) { - throw new RestException(401); - } - - $filename = basename($original_file); - $original_file_osencoded = dol_osencode($original_file); // New file name encoded in OS encoding charset - - if (!file_exists($original_file_osencoded)) { - dol_syslog("Try to download not found file ".$original_file_osencoded, LOG_WARNING); - throw new RestException(404, 'File not found'); - } - - if (@unlink($original_file_osencoded)) { - return array( - 'success' => array( - 'code' => 200, - 'message' => 'Document deleted' - ) - ); - } - - throw new RestException(401); + global $conf, $langs; + + if (empty($modulepart)) { + throw new RestException(400, 'bad value for parameter modulepart'); + } + if (empty($original_file)) { + throw new RestException(400, 'bad value for parameter original_file'); + } + + //--- Finds and returns the document + $entity = $conf->entity; + + // Special cases that need to use get_exdir to get real dir of object + // If future, all object should use this to define path of documents. + /* + $tmpreldir = ''; + if ($modulepart == 'supplier_invoice') { + $tmpreldir = get_exdir($object->id, 2, 0, 0, $object, 'invoice_supplier'); + } + + $relativefile = $tmpreldir.dol_sanitizeFileName($object->ref); */ + $relativefile = $original_file; + + $check_access = dol_check_secure_access_document($modulepart, $relativefile, $entity, DolibarrApiAccess::$user, '', 'read'); + $accessallowed = $check_access['accessallowed']; + $sqlprotectagainstexternals = $check_access['sqlprotectagainstexternals']; + $original_file = $check_access['original_file']; + + if (preg_match('/\.\./', $original_file) || preg_match('/[<>|]/', $original_file)) { + throw new RestException(401); + } + if (!$accessallowed) { + throw new RestException(401); + } + + $filename = basename($original_file); + $original_file_osencoded = dol_osencode($original_file); // New file name encoded in OS encoding charset + + if (!file_exists($original_file_osencoded)) + { + dol_syslog("Try to download not found file ".$original_file_osencoded, LOG_WARNING); + throw new RestException(404, 'File not found'); + } + + if (@unlink($original_file_osencoded)) { + return array( + 'success' => array( + 'code' => 200, + 'message' => 'Document deleted' + ) + ); + } + + throw new RestException(401); @@ -996 +788 @@ - // phpcs:disable PEAR.NamingConventions.ValidFunctionName + // phpcs:disable PEAR.NamingConventions.ValidFunctionName @@ -1004,3 +796,3 @@ - private function _validate_file($data) - { - // phpcs:enable + private function _validate_file($data) + { + // phpcs:enable @@ -1009 +801 @@ - if (!isset($data[$field])) { + if (!isset($data[$field])) @@ -1011 +802,0 @@ - } --- /tmp/dsg/dolibarr/htdocs/api/class/github_19.0.3_api_login.class.php +++ /tmp/dsg/dolibarr/htdocs/api/class/client_api_login.class.php @@ -21 +20,0 @@ -require_once DOL_DOCUMENT_ROOT.'/core/lib/security.lib.php'; @@ -29,4 +27,0 @@ - /** - * @var DoliDB Database handler - */ - public $db; @@ -34,13 +29,8 @@ - /** - * Constructor of the class - */ - public function __construct() - { - global $conf, $db; - $this->db = $db; - - //$conf->global->MAIN_MODULE_API_LOGIN_DISABLED = 1; - if (getDolGlobalString('MAIN_MODULE_API_LOGIN_DISABLED')) { - throw new RestException(403, "Error login APIs are disabled. You must get the token from backoffice to be able to use APIs"); - } - } + /** + * Constructor of the class + */ + public function __construct() + { + global $db; + $this->db = $db; + } @@ -52,3 +42,2 @@ - * WARNING: You should NEVER use this API, like you should never use the similare API that uses the POST method. This will expose your password. - * To use the APIs, you should instead set an API token to the user you want to allow to use API (This API token called DOLAPIKEY can be found/set on the user page) and use this token as credential for any API call. - * From the API explorer, you can enter directly the "DOLAPIKEY" into the field at the top right of the page to get access to any allowed APIs. + * Using method POST is recommanded for security reasons (method GET is often logged by default by web servers with parameters so with login and pass into server log file). + * Both methods are provided for developer conveniance. Best is to not use at all the login API method and enter directly the "DOLAPIKEY" into field at the top right of page. Note: The API key (DOLAPIKEY) can be found/set on the user page. @@ -60,4 +49,4 @@ - * @return array Response status and user token - * - * @throws RestException 403 Access denied - * @throws RestException 500 System error + * @return array Response status and user token + * + * @throws RestException 403 + * @throws RestException 500 @@ -66,23 +54,0 @@ - */ - public function loginUnsecured($login, $password, $entity = '', $reset = 0) - { - return $this->index($login, $password, $entity, $reset); - } - - /** - * Login - * - * Request the API token for a couple username / password. - * WARNING: You should NEVER use this API, like you should never use the similare API that uses the POST method. This will expose your password. - * To use the APIs, you should instead set an API token to the user you want to allow to use API (This API token called DOLAPIKEY can be found/set on the user page) and use this token as credential for any API call. - * From the API explorer, you can enter directly the "DOLAPIKEY" into the field at the top right of the page to get access to any allowed APIs. - * - * @param string $login User login - * @param string $password User password - * @param string $entity Entity (when multicompany module is used). '' means 1=first company. - * @param int $reset Reset token (0=get current token, 1=ask a new token and canceled old token. This means access using current existing API token of user will fails: new token will be required for new access) - * @return array Response status and user token - * - * @throws RestException 403 Access denied - * @throws RestException 500 System error - * @@ -91,3 +57,3 @@ - public function index($login, $password, $entity = '', $reset = 0) - { - global $conf, $dolibarr_main_authentication, $dolibarr_auto_user; + public function index($login, $password, $entity = '', $reset = 0) + { + global $conf, $dolibarr_main_authentication, $dolibarr_auto_user; @@ -95,5 +61 @@ - // Is the login API disabled ? The token must be generated from backoffice only. - if (getDolGlobalString('API_DISABLE_LOGIN_API')) { - dol_syslog("Warning: A try to use the login API has been done while the login API is disabled. You must generate or get the token from the backoffice.", LOG_WARNING); - throw new RestException(403, "Error, the login API has been disabled for security purpose. You must generate or get the token from the backoffice."); - } + // TODO Remove the API login. The token must be generated from backoffice only. @@ -101,4 +63,2 @@ - // Authentication mode - if (empty($dolibarr_main_authentication)) { - $dolibarr_main_authentication = 'dolibarr'; - } + // Authentication mode + if (empty($dolibarr_main_authentication)) $dolibarr_main_authentication = 'dolibarr'; @@ -107,5 +67,5 @@ - if ($dolibarr_main_authentication == 'forceuser') { - if (empty($dolibarr_auto_user)) { - $dolibarr_auto_user = 'auto'; - } - if ($dolibarr_auto_user != $login) { + if ($dolibarr_main_authentication == 'forceuser') + { + if (empty($dolibarr_auto_user)) $dolibarr_auto_user = 'auto'; + if ($dolibarr_auto_user != $login) + { @@ -120 +80,2 @@ - if ($entity != '' && !is_numeric($entity)) { + if ($entity != '' && !is_numeric($entity)) + { @@ -123,3 +84 @@ - if ($entity == '') { - $entity = 1; - } + if ($entity == '') $entity = 1; @@ -128,5 +87,3 @@ - $login = checkLoginPassEntity($login, $password, $entity, $authmode, 'api'); // Check credentials. - if ($login === '--bad-login-validity--') { - $login = ''; - } - if (empty($login)) { + $login = checkLoginPassEntity($login, $password, $entity, $authmode, 'api'); + if (empty($login)) + { @@ -140 +97,2 @@ - if (empty($tmpuser->id)) { + if (empty($tmpuser->id)) + { @@ -145 +103,2 @@ - if (empty($tmpuser->api_key) || $reset) { + if (empty($tmpuser->api_key) || $reset) + { @@ -147,6 +106,3 @@ - if (!$tmpuser->hasRight('user', 'self', 'creer')) { - if (empty($tmpuser->api_key)) { - throw new RestException(403, 'No API token set for this user and user need write permission on itself to reset its API token'); - } else { - throw new RestException(403, 'User need write permission on itself to reset its API token'); - } + if (empty($tmpuser->rights->user->self->creer)) + { + throw new RestException(403, 'User need write permission on itself to reset its API token'); @@ -155,2 +111,2 @@ - // Generate token for user - $token = dol_hash($login.uniqid().(!getDolGlobalString('MAIN_API_KEY') ? '' : $conf->global->MAIN_API_KEY), 1); + // Generate token for user + $token = dol_hash($login.uniqid().$conf->global->MAIN_API_KEY, 1); @@ -158,4 +114,4 @@ - // We store API token into database - $sql = "UPDATE ".MAIN_DB_PREFIX."user"; - $sql .= " SET api_key = '".$this->db->escape(dolEncrypt($token, '', '', 'dolibarr'))."'"; - $sql .= " WHERE login = '".$this->db->escape($login)."'"; + // We store API token into database + $sql = "UPDATE ".MAIN_DB_PREFIX."user"; + $sql .= " SET api_key = '".$this->db->escape($token)."'"; + $sql .= " WHERE login = '".$this->db->escape($login)."'"; @@ -163,10 +119,6 @@ - dol_syslog(get_class($this)."::login", LOG_DEBUG); // No log - $result = $this->db->query($sql); - if (!$result) { - throw new RestException(500, 'Error when updating api_key for user :'.$this->db->lasterror()); - } - } else { - $token = $tmpuser->api_key; - if (!utf8_check($token)) { - throw new RestException(500, 'Error, the API token of this user has a non valid value. Try to update it with a valid value.'); - } + dol_syslog(get_class($this)."::login", LOG_DEBUG); // No log + $result = $this->db->query($sql); + if (!$result) + { + throw new RestException(500, 'Error when updating api_key for user :'.$this->db->lasterror()); + } @@ -174,3 +126,3 @@ - - if (!ascii_check($token)) { - throw new RestException(500, 'Error the token for this user has not an hexa format. Try first to reset it.'); + else + { + $token = $tmpuser->api_key; @@ -184,2 +136,2 @@ - 'entity' => $tmpuser->entity, - 'message' => 'Welcome '.$login.($reset ? ' - Token is new' : ' - This is your token (recorded for your user). You can use it to make any REST API call, or enter it into the DOLAPIKEY field to use the Dolibarr API explorer.') + 'entity' => $tmpuser->entity, + 'message' => 'Welcome '.$login.($reset ? ' - Token is new' : ' - This is your token (generated by a previous call). You can use it to make any REST API call, or enter it into the DOLAPIKEY field to use the Dolibarr API explorer.') @@ -188 +140 @@ - } + } --- /tmp/dsg/dolibarr/htdocs/api/class/github_19.0.3_api_setup.class.php +++ /tmp/dsg/dolibarr/htdocs/api/class/client_api_setup.class.php @@ -6,2 +6,2 @@ - * Copyright (C) 2018-2021 Frédéric France - * Copyright (C) 2018-2022 Thibault FOUCART + * Copyright (C) 2018 Frédéric France + * Copyright (C) 2018-2019 Thibault FOUCART @@ -27,3 +26,0 @@ -require_once DOL_DOCUMENT_ROOT.'/api/class/api.class.php'; -require_once DOL_DOCUMENT_ROOT.'/core/class/cstate.class.php'; -require_once DOL_DOCUMENT_ROOT.'/core/class/cregion.class.php'; @@ -31 +27,0 @@ -require_once DOL_DOCUMENT_ROOT.'/hrm/class/establishment.class.php'; @@ -41,29 +37,1128 @@ - private $translations = null; - - /** - * Constructor - */ - public function __construct() - { - global $db; - $this->db = $db; - } - - /** - * Get the list of ordering methods. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number {@min 0} - * @param int $active Payment type is active or not {@min 0} {@max 1} - * @param string $sqlfilters SQL criteria to filter with. Syntax example "(t.code:=:'OrderByWWW')" - * - * @url GET dictionary/ordering_methods - * - * @return array [List of ordering methods] - * - * @throws RestException 400 - */ - public function getOrderingMethods($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '') - { + private $translations = null; + + /** + * Constructor + */ + public function __construct() + { + global $db; + $this->db = $db; + } + + /** + * Get the list of ordering methods. + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Number of items per page + * @param int $page Page number {@min 0} + * @param int $active Payment type is active or not {@min 0} {@max 1} + * @param string $sqlfilters SQL criteria to filter with. Syntax example "(t.code:=:'OrderByWWW')" + * + * @url GET dictionary/ordering_methods + * + * @return array [List of ordering methods] + * + * @throws RestException 400 + */ + public function getOrderingMethods($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '') + { + $list = array(); + + $sql = "SELECT rowid, code, libelle as label, module"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_input_method as t"; + $sql .= " WHERE t.active = ".$active; + // Add sql filters + if ($sqlfilters) + { + if (!DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(400, 'error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + + $sql .= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $list[] = $this->db->fetch_object($result); + } + } else { + throw new RestException(400, $this->db->lasterror()); + } + + return $list; + } + + /** + * Get the list of payments types. + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Number of items per page + * @param int $page Page number {@min 0} + * @param int $active Payment type is active or not {@min 0} {@max 1} + * @param string $sqlfilters SQL criteria to filter with. Syntax example "(t.code:=:'CHQ')" + * + * @url GET dictionary/payment_types + * + * @return array [List of payment types] + * + * @throws RestException 400 + */ + public function getPaymentTypes($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '') + { + $list = array(); + + $sql = "SELECT id, code, type, libelle as label, module"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_paiement as t"; + $sql .= " WHERE t.entity IN (".getEntity('c_paiement').")"; + $sql .= " AND t.active = ".$active; + // Add sql filters + if ($sqlfilters) + { + if (!DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(400, 'error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + + $sql .= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $list[] = $this->db->fetch_object($result); + } + } else { + throw new RestException(400, $this->db->lasterror()); + } + + return $list; + } + + /** + * Get the list of countries. + * + * The names of the countries will be translated to the given language if + * the $lang parameter is provided. The value of $lang must be a language + * code supported by Dolibarr, for example 'en_US' or 'fr_FR'. + * The returned list is sorted by country ID. + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Number of items per page + * @param int $page Page number (starting from zero) + * @param string $filter To filter the countries by name + * @param string $lang Code of the language the label of the countries must be translated to + * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" + * @return array List of countries + * + * @url GET dictionary/countries + * + * @throws RestException + */ + public function getListOfCountries($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $filter = '', $lang = '', $sqlfilters = '') + { + $list = array(); + + // Note: The filter is not applied in the SQL request because it must + // be applied to the translated names, not to the names in database. + $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."c_country as t"; + $sql .= " WHERE 1 = 1"; + // Add sql filters + if ($sqlfilters) + { + if (!DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + $sql .= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $obj = $this->db->fetch_object($result); + $country = new Ccountry($this->db); + if ($country->fetch($obj->rowid) > 0) { + // Translate the name of the country if needed + // and then apply the filter if there is one. + $this->translateLabel($country, $lang, 'Country'); + + if (empty($filter) || stripos($country->label, $filter) !== false) { + $list[] = $this->_cleanObjectDatas($country); + } + } + } + } else { + throw new RestException(503, 'Error when retrieving list of countries'); + } + + return $list; + } + + /** + * Get country by ID. + * + * @param int $id ID of country + * @param string $lang Code of the language the name of the + * country must be translated to + * @return array Array of cleaned object properties + * + * @url GET dictionary/countries/{id} + * + * @throws RestException + */ + public function getCountryByID($id, $lang = '') + { + return $this->_fetchCcountry($id, '', '', $lang); + } + + /** + * Get country by Code. + * + * @param string $code Code of country + * @param string $lang Code of the language the name of the + * country must be translated to + * @return array Array of cleaned object properties + * + * @url GET dictionary/countries/byCode/{code} + * + * @throws RestException + */ + public function getCountryByCode($code, $lang = '') + { + return $this->_fetchCcountry('', $code, '', $lang); + } + + /** + * Get country by Iso. + * + * @param string $iso ISO of country + * @param string $lang Code of the language the name of the + * country must be translated to + * @return array Array of cleaned object properties + * + * @url GET dictionary/countries/byISO/{iso} + * + * @throws RestException + */ + public function getCountryByISO($iso, $lang = '') + { + return $this->_fetchCcountry('', '', $iso, $lang); + } + + /** + * Get country. + * + * @param int $id ID of country + * @param string $code Code of country + * @param string $iso ISO of country + * @param string $lang Code of the language the name of the + * country must be translated to + * @return array Array of cleaned object properties + * + * @throws RestException + */ + private function _fetchCcountry($id, $code = '', $iso = '', $lang = '') + { + $country = new Ccountry($this->db); + + $result = $country->fetch($id, $code, $iso); + if ($result < 0) { + throw new RestException(503, 'Error when retrieving country : '.$country->error); + } elseif ($result == 0) { + throw new RestException(404, 'country not found'); + } + + $this->translateLabel($country, $lang, 'Country'); + + return $this->_cleanObjectDatas($country); + } + + /** + * Get the list of delivery times. + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Number of items per page + * @param int $page Page number {@min 0} + * @param int $active Delivery times is active or not {@min 0} {@max 1} + * @param string $sqlfilters SQL criteria to filter with. + * + * @url GET dictionary/availability + * + * @return array [List of availability] + * + * @throws RestException 400 + */ + public function getAvailability($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '') + { + $list = array(); + + $sql = "SELECT rowid, code, label"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_availability as t"; + $sql .= " WHERE t.active = ".$active; + // Add sql filters + if ($sqlfilters) + { + if (!DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(400, 'error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + + $sql .= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $list[] = $this->db->fetch_object($result); + } + } else { + throw new RestException(400, $this->db->lasterror()); + } + + return $list; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore + /** + * Clean sensible object datas + * + * @param object $object Object to clean + * @return array Array of cleaned object properties + */ + protected function _cleanObjectDatas($object) + { + // phpcs:enable + $object = parent::_cleanObjectDatas($object); + + unset($object->error); + unset($object->errors); + + return $object; + } + + /** + * Translate the name of the object to the given language. + * + * @param object $object Object with label to translate + * @param string $lang Code of the language the name of the object must be translated to + * @param string $prefix Prefix for translation key + * + * @return void + */ + private function translateLabel($object, $lang, $prefix = 'Country') + { + if (!empty($lang)) { + // Load the translations if this is a new language. + if ($this->translations == null || $this->translations->getDefaultLang() !== $lang) { + global $conf; + $this->translations = new Translate('', $conf); + $this->translations->setDefaultLang($lang); + $this->translations->load('dict'); + } + if ($object->code) { + $key = $prefix.$object->code; + + $translation = $this->translations->trans($key); + if ($translation != $key) { + $object->label = html_entity_decode($translation); + } + } + } + } + + /** + * Get the list of shipment methods. + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Number of items per page + * @param int $page Page number (starting from zero) + * @param int $active Payment term is active or not {@min 0} {@max 1} + * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" + * + * @return array List of shipment methods + * + * @url GET dictionary/shipment_methods + * + * @throws RestException + */ + public function getListOfShipmentMethods($sortfield = "rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '') + { + $list = array(); + $sql = "SELECT t.rowid, t.code, t.libelle, t.description, t.tracking"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as t"; + $sql .= " WHERE t.active = ".$active; + // Add sql filters + if ($sqlfilters) + { + if (!DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + + $sql .= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $list[] = $this->db->fetch_object($result); + } + } else { + throw new RestException(503, 'Error when retrieving list of shipment methods : '.$this->db->lasterror()); + } + + return $list; + } + + /** + * Get the list of events types. + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Number of items per page + * @param int $page Page number (starting from zero) + * @param string $type To filter on type of event + * @param string $module To filter on module events + * @param int $active Event's type is active or not {@min 0} {@max 1} + * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" + * @return array List of events types + * + * @url GET dictionary/event_types + * + * @throws RestException + */ + public function getListOfEventTypes($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $type = '', $module = '', $active = 1, $sqlfilters = '') + { + $list = array(); + + $sql = "SELECT id, code, type, libelle as label, module"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_actioncomm as t"; + $sql .= " WHERE t.active = ".$active; + if ($type) $sql .= " AND t.type LIKE '%".$this->db->escape($type)."%'"; + if ($module) $sql .= " AND t.module LIKE '%".$this->db->escape($module)."%'"; + // Add sql filters + if ($sqlfilters) + { + if (!DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + + $sql .= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $list[] = $this->db->fetch_object($result); + } + } else { + throw new RestException(503, 'Error when retrieving list of events types : '.$this->db->lasterror()); + } + + return $list; + } + + + /** + * Get the list of Expense Report types. + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Number of items per page + * @param int $page Page number (starting from zero) + * @param string $module To filter on module + * @param int $active Event's type is active or not {@min 0} {@max 1} + * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" + * @return array List of expense report types + * + * @url GET dictionary/expensereport_types + * + * @throws RestException + */ + public function getListOfExpenseReportsTypes($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $module = '', $active = 1, $sqlfilters = '') + { + $list = array(); + + $sql = "SELECT id, code, label, accountancy_code, active, module, position"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_type_fees as t"; + $sql .= " WHERE t.active = ".$active; + if ($module) $sql .= " AND t.module LIKE '%".$this->db->escape($module)."%'"; + // Add sql filters + if ($sqlfilters) + { + if (!DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + + $sql .= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $list[] = $this->db->fetch_object($result); + } + } else { + throw new RestException(503, 'Error when retrieving list of expense report types : '.$this->db->lasterror()); + } + + return $list; + } + + + /** + * Get the list of contacts types. + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Number of items per page + * @param int $page Page number (starting from zero) + * @param string $type To filter on type of contact + * @param string $module To filter on module contacts + * @param int $active Contact's type is active or not {@min 0} {@max 1} + * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" + * @return array List of Contacts types + * + * @url GET dictionary/contact_types + * + * @throws RestException + */ + public function getListOfContactTypes($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $type = '', $module = '', $active = 1, $sqlfilters = '') + { + $list = array(); + + $sql = "SELECT rowid, code, element as type, libelle as label, source, module, position"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact as t"; + $sql .= " WHERE t.active = ".$active; + if ($type) $sql .= " AND type LIKE '%".$this->db->escape($type)."%'"; + if ($module) $sql .= " AND t.module LIKE '%".$this->db->escape($module)."%'"; + // Add sql filters + if ($sqlfilters) + { + if (!DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + + $sql .= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $list[] = $this->db->fetch_object($result); + } + } else { + throw new RestException(503, 'Error when retrieving list of contacts types : '.$this->db->lasterror()); + } + + return $list; + } + + /** + * Get the list of civilities. + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Number of items per page + * @param int $page Page number (starting from zero) + * @param string $module To filter on module events + * @param int $active Civility is active or not {@min 0} {@max 1} + * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" + * @return array List of civility types + * + * @url GET dictionary/civilities + * + * @throws RestException + */ + public function getListOfCivilities($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $module = '', $active = 1, $sqlfilters = '') + { + $list = array(); + + $sql = "SELECT rowid, code, label, module"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_civility as t"; + $sql .= " WHERE t.active = ".$active; + if ($module) $sql .= " AND t.module LIKE '%".$this->db->escape($module)."%'"; + // Add sql filters + if ($sqlfilters) + { + if (!DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + + $sql .= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $list[] = $this->db->fetch_object($result); + } + } else { + throw new RestException(503, 'Error when retrieving list of civility : '.$this->db->lasterror()); + } + + return $list; + } + + /** + * Get the list of currencies. + * + * @param int $multicurrency Multicurrency rates (0: no multicurrency, 1: last rate, 2: all rates) {@min 0} {@max 2} + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Number of items per page + * @param int $page Page number (starting from zero) + * @param int $active Payment term is active or not {@min 0} {@max 1} + * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" + * @return array List of currencies + * + * @url GET dictionary/currencies + * + * @throws RestException + */ + public function getListOfCurrencies($multicurrency = 0, $sortfield = "code_iso", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '') + { + $list = array(); + $sql = "SELECT t.code_iso, t.label, t.unicode"; + if (!empty($multicurrency)) $sql .= " , cr.date_sync, cr.rate "; + $sql .= " FROM ".MAIN_DB_PREFIX."c_currencies as t"; + if (!empty($multicurrency)) { + $sql .= " JOIN ".MAIN_DB_PREFIX."multicurrency as m ON m.code=t.code_iso"; + $sql .= " JOIN ".MAIN_DB_PREFIX."multicurrency_rate as cr ON (m.rowid = cr.fk_multicurrency)"; + } + $sql .= " WHERE t.active = ".$active; + if (!empty($multicurrency)) { + $sql .= " AND m.entity IN (".getEntity('multicurrency').")"; + if (!empty($multicurrency) && $multicurrency != 2) { + $sql .= " AND cr.date_sync = (SELECT MAX(cr2.date_sync) FROM ".MAIN_DB_PREFIX."multicurrency_rate AS cr2 WHERE cr2.fk_multicurrency = m.rowid)"; + } + } + + // Add sql filters + if ($sqlfilters) + { + if (!DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + + $sql .= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $list[] = $this->db->fetch_object($result); + } + } else { + throw new RestException(503, 'Error when retrieving list of currency : '.$this->db->lasterror()); + } + + return $list; + } + + /** + * Get the list of extra fields. + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param string $type Type of element ('adherent', 'commande', 'thirdparty', 'facture', 'propal', 'product', ...) + * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.label:like:'SO-%')" + * @return array List of extra fields + * + * @url GET extrafields + * + * @throws RestException + */ + public function getListOfExtrafields($sortfield = "t.pos", $sortorder = 'ASC', $type = '', $sqlfilters = '') + { + $list = array(); + + if ($type == 'thirdparty') $type = 'societe'; + if ($type == 'contact') $type = 'socpeople'; + + $sql = "SELECT t.rowid, t.name, t.label, t.type, t.size, t.elementtype, t.fieldunique, t.fieldrequired, t.param, t.pos, t.alwayseditable, t.perms, t.list, t.fielddefault, t.fieldcomputed"; + $sql .= " FROM ".MAIN_DB_PREFIX."extrafields as t"; + $sql .= " WHERE t.entity IN (".getEntity('extrafields').")"; + if (!empty($type)) $sql .= " AND t.elementtype = '".$this->db->escape($type)."'"; + // Add sql filters + if ($sqlfilters) + { + if (!DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + $sql .= $this->db->order($sortfield, $sortorder); + + $resql = $this->db->query($sql); + if ($resql) + { + if ($this->db->num_rows($resql)) + { + while ($tab = $this->db->fetch_object($resql)) + { + // New usage + $list[$tab->elementtype][$tab->name]['type'] = $tab->type; + $list[$tab->elementtype][$tab->name]['label'] = $tab->label; + $list[$tab->elementtype][$tab->name]['size'] = $tab->size; + $list[$tab->elementtype][$tab->name]['elementtype'] = $tab->elementtype; + $list[$tab->elementtype][$tab->name]['default'] = $tab->fielddefault; + $list[$tab->elementtype][$tab->name]['computed'] = $tab->fieldcomputed; + $list[$tab->elementtype][$tab->name]['unique'] = $tab->fieldunique; + $list[$tab->elementtype][$tab->name]['required'] = $tab->fieldrequired; + $list[$tab->elementtype][$tab->name]['param'] = ($tab->param ? unserialize($tab->param) : ''); + $list[$tab->elementtype][$tab->name]['pos'] = $tab->pos; + $list[$tab->elementtype][$tab->name]['alwayseditable'] = $tab->alwayseditable; + $list[$tab->elementtype][$tab->name]['perms'] = $tab->perms; + $list[$tab->elementtype][$tab->name]['list'] = $tab->list; + } + } + } + else + { + throw new RestException(503, 'Error when retrieving list of extra fields : '.$this->db->lasterror()); + } + + if (!count($list)) + { + throw new RestException(404, 'No extrafield found'); + } + + return $list; + } + + + /** + * Get the list of towns. + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Number of items per page + * @param int $page Page number (starting from zero) + * @param string $zipcode To filter on zipcode + * @param string $town To filter on city name + * @param int $active Payment term is active or not {@min 0} {@max 1} + * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" + * @return array List of towns + * + * @url GET dictionary/towns + * + * @throws RestException + */ + public function getListOfTowns($sortfield = "zip,town", $sortorder = 'ASC', $limit = 100, $page = 0, $zipcode = '', $town = '', $active = 1, $sqlfilters = '') + { + $list = array(); + + $sql = "SELECT rowid AS id, zip, town, fk_county, fk_pays AS fk_country"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_ziptown as t"; + $sql .= " AND t.active = ".$active; + if ($zipcode) $sql .= " AND t.zip LIKE '%".$this->db->escape($zipcode)."%'"; + if ($town) $sql .= " AND t.town LIKE '%".$this->db->escape($town)."%'"; + // Add sql filters + if ($sqlfilters) + { + if (!DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + + $sql .= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $list[] = $this->db->fetch_object($result); + } + } else { + throw new RestException(503, 'Error when retrieving list of towns : '.$this->db->lasterror()); + } + + return $list; + } + + /** + * Get the list of payments terms. + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Number of items per page + * @param int $page Page number {@min 0} + * @param int $active Payment term is active or not {@min 0} {@max 1} + * @param string $sqlfilters SQL criteria to filter. Syntax example "(t.code:=:'CHQ')" + * + * @url GET dictionary/payment_terms + * + * @return array List of payment terms + * + * @throws RestException 400 + */ + public function getPaymentTerms($sortfield = "sortorder", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '') + { + $list = array(); + + $sql = "SELECT rowid as id, code, sortorder, libelle as label, libelle_facture as descr, type_cdr, nbjour, decalage, module"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_payment_term as t"; + $sql .= " WHERE t.entity IN (".getEntity('c_payment_term').")"; + $sql .= " AND t.active = ".$active; + // Add sql filters + if ($sqlfilters) + { + if (!DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(400, 'Error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + + $sql .= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $list[] = $this->db->fetch_object($result); + } + } else { + throw new RestException(400, $this->db->lasterror()); + } + + return $list; + } + + /** + * Get the list of shipping methods. + * + * @param int $limit Number of items per page + * @param int $page Page number {@min 0} + * @param int $active Shipping methodsm is active or not {@min 0} {@max 1} + * @param string $sqlfilters SQL criteria to filter. Syntax example "(t.code:=:'CHQ')" + * + * @url GET dictionary/shipping_methods + * + * @return array List of shipping methods + * + * @throws RestException 400 + */ + public function getShippingModes($limit = 100, $page = 0, $active = 1, $sqlfilters = '') + { + $list = array(); + + $sql = "SELECT rowid as id, code, libelle as label, description, tracking, module"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as t"; + $sql .= " WHERE t.entity IN (".getEntity('c_shipment_mode').")"; + $sql .= " AND t.active = ".$active; + // Add sql filters + if ($sqlfilters) + { + if (!DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(400, 'Error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + + //$sql.= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $list[] = $this->db->fetch_object($result); + } + } else { + throw new RestException(400, $this->db->lasterror()); + } + + return $list; + } + + /** + * Get the list of measuring units. + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Number of items per page + * @param int $page Page number (starting from zero) + * @param int $active Measuring unit is active or not {@min 0} {@max 1} + * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" + * @return array List of measuring unit + * + * @url GET dictionary/units + * + * @throws RestException + */ + public function getListOfMeasuringUnits($sortfield = "rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '') + { + $list = array(); + //TODO link with multicurrency module + $sql = "SELECT t.rowid, t.code, t.label,t.short_label, t.active, t.scale, t.unit_type"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_units as t"; + $sql .= " WHERE t.active = ".$active; + // Add sql filters + if ($sqlfilters) + { + if (!DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + + $sql .= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $list[] = $this->db->fetch_object($result); + } + } else { + throw new RestException(503, 'Error when retrieving list of measuring units: '.$this->db->lasterror()); + } + + return $list; + } + + /** + * Get the list of social networks. + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Number of items per page + * @param int $page Page number (starting from zero) + * @param int $active Social network is active or not {@min 0} {@max 1} + * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" + * @return array List of social networks + * + * @url GET dictionary/socialnetworks + * + * @throws RestException + */ + public function getListOfsocialNetworks($sortfield = "rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '') + { + global $conf; + + if (empty($conf->socialnetworks->enabled)) { + throw new RestException(400, 'API not available: this dictionary is not enabled by setup'); + } + @@ -71,2261 +1166,611 @@ - - if (!DolibarrApiAccess::$user->hasRight('commande', 'lire')) { - throw new RestException(401); - } - - $sql = "SELECT rowid, code, libelle as label, module"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_input_method as t"; - $sql .= " WHERE t.active = ".((int) $active); - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(503, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $list[] = $this->db->fetch_object($result); - } - } else { - throw new RestException(400, $this->db->lasterror()); - } - - return $list; - } - - /** - * Get the list of ordering origins. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number {@min 0} - * @param int $active Payment type is active or not {@min 0} {@max 1} - * @param string $sqlfilters SQL criteria to filter with. Syntax example "(t.code:=:'OrderByWWW')" - * @return array [List of ordering reasons] - * - * @url GET dictionary/ordering_origins - * - * @throws RestException 400 - */ - public function getOrderingOrigins($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '') - { - $list = array(); - - if (!DolibarrApiAccess::$user->hasRight('commande', 'lire')) { - throw new RestException(401); - } - - $sql = "SELECT rowid, code, label, module"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_input_reason as t"; - $sql .= " WHERE t.active = ".((int) $active); - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $list[] = $this->db->fetch_object($result); - } - } else { - throw new RestException(400, $this->db->lasterror()); - } - - return $list; - } - - /** - * Get the list of payments types. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number {@min 0} - * @param int $active Payment type is active or not {@min 0} {@max 1} - * @param string $sqlfilters SQL criteria to filter with. Syntax example "(t.code:=:'CHQ')" - * - * @url GET dictionary/payment_types - * - * @return array [List of payment types] - * - * @throws RestException 400 - */ - public function getPaymentTypes($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '') - { - $list = array(); - - if (!DolibarrApiAccess::$user->hasRight('propal', 'lire') && !DolibarrApiAccess::$user->hasRight('commande', 'lire') && !DolibarrApiAccess::$user->hasRight('facture', 'lire')) { - throw new RestException(401); - } - - $sql = "SELECT id, code, type, libelle as label, module"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_paiement as t"; - $sql .= " WHERE t.entity IN (".getEntity('c_paiement').")"; - $sql .= " AND t.active = ".((int) $active); - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $list[] = $this->db->fetch_object($result); - } - } else { - throw new RestException(400, $this->db->lasterror()); - } - - return $list; - } - /** - * Get the list of regions. - * - * The returned list is sorted by region ID. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number (starting from zero) - * @param int $country To filter on country - * @param string $filter To filter the regions by name - * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" - * @return array List of regions - * - * @url GET dictionary/regions - * - * @throws RestException - */ - public function getListOfRegions($sortfield = "code_region", $sortorder = 'ASC', $limit = 100, $page = 0, $country = 0, $filter = '', $sqlfilters = '') - { - $list = array(); - - // Note: The filter is not applied in the SQL request because it must - // be applied to the translated names, not to the names in database. - $sql = "SELECT t.rowid FROM ".MAIN_DB_PREFIX."c_regions as t"; - $sql .= " WHERE 1 = 1"; - if ($country) { - $sql .= " AND t.fk_pays = ".((int) $country); - } - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - if (!DolibarrApi::_checkFilters($sqlfilters, $errormessage)) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)'; - $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; - } - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $obj = $this->db->fetch_object($result); - $region = new Cregion($this->db); - if ($region->fetch($obj->rowid) > 0) { - if (empty($filter) || stripos($region->name, $filter) !== false) { - $list[] = $this->_cleanObjectDatas($region); - } - } - } - } else { - throw new RestException(503, 'Error when retrieving list of regions'); - } - - return $list; - } - - /** - * Get region by ID. - * - * @param int $id ID of region - * @return Object Object with cleaned properties - * - * @url GET dictionary/regions/{id} - * - * @throws RestException - */ - public function getRegionByID($id) - { - return $this->_fetchCregion($id, ''); - } - - /** - * Get region by Code. - * - * @param string $code Code of region - * @return Object Object with cleaned properties - * - * @url GET dictionary/regions/byCode/{code} - * - * @throws RestException - */ - public function getRegionByCode($code) - { - return $this->_fetchCregion('', $code); - } - - /** - * Get the list of states/provinces. - * - * The names of the states will be translated to the given language if - * the $lang parameter is provided. The value of $lang must be a language - * code supported by Dolibarr, for example 'en_US' or 'fr_FR'. - * The returned list is sorted by state ID. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number (starting from zero) - * @param int $country To filter on country - * @param string $filter To filter the states by name - * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" - * @return array List of states - * - * @url GET dictionary/states - * - * @throws RestException - */ - public function getListOfStates($sortfield = "code_departement", $sortorder = 'ASC', $limit = 100, $page = 0, $country = 0, $filter = '', $sqlfilters = '') - { - $list = array(); - - // Note: The filter is not applied in the SQL request because it must - // be applied to the translated names, not to the names in database. - $sql = "SELECT t.rowid FROM ".MAIN_DB_PREFIX."c_departements as t"; - if ($country) { - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_regions as d ON t.fk_region = d.code_region"; - } - $sql .= " WHERE 1 = 1"; - if ($country) { - $sql .= " AND d.fk_pays = ".((int) $country); - } - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $obj = $this->db->fetch_object($result); - $state = new Cstate($this->db); - if ($state->fetch($obj->rowid) > 0) { - if (empty($filter) || stripos($state->label, $filter) !== false) { - $list[] = $this->_cleanObjectDatas($state); - } - } - } - } else { - throw new RestException(503, 'Error when retrieving list of states'); - } - - return $list; - } - - /** - * Get state by ID. - * - * @param int $id ID of state - * @return Object Object with cleaned properties - * - * @url GET dictionary/states/{id} - * - * @throws RestException - */ - public function getStateByID($id) - { - return $this->_fetchCstate($id, ''); - } - - /** - * Get state by Code. - * - * @param string $code Code of state - * @return Object Object with cleaned properties - * - * @url GET dictionary/states/byCode/{code} - * - * @throws RestException - */ - public function getStateByCode($code) - { - return $this->_fetchCstate('', $code); - } - - /** - * Get the list of countries. - * - * The names of the countries will be translated to the given language if - * the $lang parameter is provided. The value of $lang must be a language - * code supported by Dolibarr, for example 'en_US' or 'fr_FR'. - * The returned list is sorted by country ID. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number (starting from zero) - * @param string $filter To filter the countries by name - * @param string $lang Code of the language the label of the countries must be translated to - * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" - * @return array List of countries - * - * @url GET dictionary/countries - * - * @throws RestException - */ - public function getListOfCountries($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $filter = '', $lang = '', $sqlfilters = '') - { - $list = array(); - - // Note: The filter is not applied in the SQL request because it must - // be applied to the translated names, not to the names in database. - $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."c_country as t"; - $sql .= " WHERE 1 = 1"; - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $obj = $this->db->fetch_object($result); - $country = new Ccountry($this->db); - if ($country->fetch($obj->rowid) > 0) { - // Translate the name of the country if needed - // and then apply the filter if there is one. - $this->translateLabel($country, $lang, 'Country'); - - if (empty($filter) || stripos($country->label, $filter) !== false) { - $list[] = $this->_cleanObjectDatas($country); - } - } - } - } else { - throw new RestException(503, 'Error when retrieving list of countries'); - } - - return $list; - } - - /** - * Get country by ID. - * - * @param int $id ID of country - * @param string $lang Code of the language the name of the country must be translated to - * @return Object Object with cleaned properties - * - * @url GET dictionary/countries/{id} - * - * @throws RestException - */ - public function getCountryByID($id, $lang = '') - { - return $this->_fetchCcountry($id, '', '', $lang); - } - - /** - * Get country by Code. - * - * @param string $code Code of country (2 characters) - * @param string $lang Code of the language the name of the country must be translated to - * @return Object Object with cleaned properties - * - * @url GET dictionary/countries/byCode/{code} - * - * @throws RestException - */ - public function getCountryByCode($code, $lang = '') - { - return $this->_fetchCcountry('', $code, '', $lang); - } - - /** - * Get country by Iso. - * - * @param string $iso ISO of country (3 characters) - * @param string $lang Code of the language the name of the country must be translated to - * @return Object Object with cleaned properties - * - * @url GET dictionary/countries/byISO/{iso} - * - * @throws RestException - */ - public function getCountryByISO($iso, $lang = '') - { - return $this->_fetchCcountry('', '', $iso, $lang); - } - - /** - * Get region. - * - * @param int $id ID of region - * @param string $code Code of region - * @return Object Object with cleaned properties - * - * @throws RestException - */ - private function _fetchCregion($id, $code = '') - { - $region = new Cregion($this->db); - - $result = $region->fetch($id, $code); - if ($result < 0) { - throw new RestException(503, 'Error when retrieving region : '.$region->error); - } elseif ($result == 0) { - throw new RestException(404, 'Region not found'); - } - - return $this->_cleanObjectDatas($region); - } - - /** - * Get state. - * - * @param int $id ID of state - * @param string $code Code of state - * @return Object Object with cleaned properties - * - * @throws RestException - */ - private function _fetchCstate($id, $code = '') - { - $state = new Cstate($this->db); - - $result = $state->fetch($id, $code); - if ($result < 0) { - throw new RestException(503, 'Error when retrieving state : '.$state->error); - } elseif ($result == 0) { - throw new RestException(404, 'State not found'); - } - - return $this->_cleanObjectDatas($state); - } - - /** - * Get country. - * - * @param int $id ID of country - * @param string $code Code of country (2 characters) - * @param string $iso ISO of country (3 characters) - * @param string $lang Code of the language the name of the country must be translated to - * @return Object Object with cleaned properties - * - * @throws RestException - */ - private function _fetchCcountry($id, $code = '', $iso = '', $lang = '') - { - $country = new Ccountry($this->db); - - $result = $country->fetch($id, $code, $iso); - - if ($result < 0) { - throw new RestException(503, 'Error when retrieving country : '.$country->error); - } elseif ($result == 0) { - throw new RestException(404, 'Country not found'); - } - - $this->translateLabel($country, $lang, 'Country'); - - return $this->_cleanObjectDatas($country); - } - - /** - * Get the list of delivery times. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number {@min 0} - * @param int $active Delivery times is active or not {@min 0} {@max 1} - * @param string $sqlfilters SQL criteria to filter with. - * - * @url GET dictionary/availability - * - * @return array [List of availability] - * - * @throws RestException 400 - */ - public function getAvailability($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '') - { - $list = array(); - - if (!DolibarrApiAccess::$user->hasRight('commande', 'lire')) { - throw new RestException(401); - } - - $sql = "SELECT rowid, code, label"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_availability as t"; - $sql .= " WHERE t.active = ".((int) $active); - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $list[] = $this->db->fetch_object($result); - } - } else { - throw new RestException(400, $this->db->lasterror()); - } - - return $list; - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore - /** - * Clean sensible object datas - * - * @param Object $object Object to clean - * @return Object Object with cleaned properties - */ - protected function _cleanObjectDatas($object) - { - // phpcs:enable - $object = parent::_cleanObjectDatas($object); - - unset($object->error); - unset($object->errors); - - return $object; - } - - /** - * Translate the name of the object to the given language. - * - * @param object $object Object with label to translate - * @param string $lang Code of the language the name of the object must be translated to - * @param string $prefix Prefix for translation key - * @param array $dict Array of dictionnary for translation - * @return void - */ - private function translateLabel($object, $lang, $prefix = 'Country', $dict = array('dict')) - { - if (!empty($lang)) { - // Load the translations if this is a new language. - if ($this->translations == null || $this->translations->getDefaultLang() !== $lang) { - global $conf; - $this->translations = new Translate('', $conf); - $this->translations->setDefaultLang($lang); - $this->translations->loadLangs($dict); - } - if ($object->code) { - $key = $prefix.$object->code; - - $translation = $this->translations->trans($key); - if ($translation != $key) { - $object->label = html_entity_decode($translation); - } - } - } - } - - /** - * Get the list of events types. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number (starting from zero) - * @param string $type To filter on type of event - * @param string $module To filter on module events - * @param int $active Event's type is active or not {@min 0} {@max 1} - * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" - * @return array List of events types - * - * @url GET dictionary/event_types - * - * @throws RestException - */ - public function getListOfEventTypes($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $type = '', $module = '', $active = 1, $sqlfilters = '') - { - $list = array(); - - $sql = "SELECT id, code, type, libelle as label, module"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_actioncomm as t"; - $sql .= " WHERE t.active = ".((int) $active); - if ($type) { - $sql .= " AND t.type LIKE '%".$this->db->escape($type)."%'"; - } - if ($module) { - $sql .= " AND t.module LIKE '%".$this->db->escape($module)."%'"; - } - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $list[] = $this->db->fetch_object($result); - } - } else { - throw new RestException(503, 'Error when retrieving list of events types : '.$this->db->lasterror()); - } - - return $list; - } - - - /** - * Get the list of Expense Report types. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number (starting from zero) - * @param string $module To filter on module - * @param int $active Event's type is active or not {@min 0} {@max 1} - * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" - * @return array List of expense report types - * - * @url GET dictionary/expensereport_types - * - * @throws RestException - */ - public function getListOfExpenseReportsTypes($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $module = '', $active = 1, $sqlfilters = '') - { - $list = array(); - - $sql = "SELECT id, code, label, accountancy_code, active, module, position"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_type_fees as t"; - $sql .= " WHERE t.active = ".((int) $active); - if ($module) { - $sql .= " AND t.module LIKE '%".$this->db->escape($module)."%'"; - } - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $list[] = $this->db->fetch_object($result); - } - } else { - throw new RestException(503, 'Error when retrieving list of expense report types : '.$this->db->lasterror()); - } - - return $list; - } - - - /** - * Get the list of contacts types. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number (starting from zero) - * @param string $type To filter on type of contact - * @param string $module To filter on module contacts - * @param int $active Contact's type is active or not {@min 0} {@max 1} - * @param string $lang Code of the language the label of the civility must be translated to - * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" - * @return array List of Contacts types - * - * @url GET dictionary/contact_types - * - * @throws RestException - */ - public function getListOfContactTypes($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $type = '', $module = '', $active = 1, $lang = '', $sqlfilters = '') - { - $list = array(); - - $sql = "SELECT rowid, code, element as type, libelle as label, source, module, position"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact as t"; - $sql .= " WHERE t.active = ".((int) $active); - if ($type) { - $sql .= " AND type LIKE '%".$this->db->escape($type)."%'"; - } - if ($module) { - $sql .= " AND t.module LIKE '%".$this->db->escape($module)."%'"; - } - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $contact_type = $this->db->fetch_object($result); - $this->translateLabel($contact_type, $lang, 'TypeContact_'.$contact_type->type.'_'.$contact_type->source.'_', array("eventorganization", "resource", "projects", "contracts", "bills", "orders", "agenda", "propal", "stocks", "supplier_proposal", "interventions", "sendings", "ticket")); - $list[] = $contact_type; - } - } else { - throw new RestException(503, 'Error when retrieving list of contacts types : '.$this->db->lasterror()); - } - - return $list; - } - - /** - * Get the list of civilities. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number (starting from zero) - * @param string $module To filter on module events - * @param int $active Civility is active or not {@min 0} {@max 1} - * @param string $lang Code of the language the label of the civility must be translated to - * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" - * @return array List of civility types - * - * @url GET dictionary/civilities - * - * @throws RestException - */ - public function getListOfCivilities($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $module = '', $active = 1, $lang = '', $sqlfilters = '') - { - $list = array(); - - $sql = "SELECT rowid, code, label, module"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_civility as t"; - $sql .= " WHERE t.active = ".((int) $active); - if ($module) { - $sql .= " AND t.module LIKE '%".$this->db->escape($module)."%'"; - } - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $civility = $this->db->fetch_object($result); - $this->translateLabel($civility, $lang, 'Civility', array('dict')); - $list[] = $civility; - } - } else { - throw new RestException(503, 'Error when retrieving list of civility : '.$this->db->lasterror()); - } - - return $list; - } - - /** - * Get the list of currencies. - * - * @param int $multicurrency Multicurrency rates (0: no multicurrency, 1: last rate, 2: all rates) {@min 0} {@max 2} - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number (starting from zero) - * @param int $active Payment term is active or not {@min 0} {@max 1} - * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" - * @return array List of currencies - * - * @url GET dictionary/currencies - * - * @throws RestException - */ - public function getListOfCurrencies($multicurrency = 0, $sortfield = "code_iso", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '') - { - $list = array(); - $sql = "SELECT t.code_iso, t.label, t.unicode"; - if (!empty($multicurrency)) { - $sql .= " , cr.date_sync, cr.rate "; - } - $sql .= " FROM ".MAIN_DB_PREFIX."c_currencies as t"; - if (!empty($multicurrency)) { - $sql .= " JOIN ".MAIN_DB_PREFIX."multicurrency as m ON m.code=t.code_iso"; - $sql .= " JOIN ".MAIN_DB_PREFIX."multicurrency_rate as cr ON (m.rowid = cr.fk_multicurrency)"; - } - $sql .= " WHERE t.active = ".((int) $active); - if (!empty($multicurrency)) { - $sql .= " AND m.entity IN (".getEntity('multicurrency').")"; - if (!empty($multicurrency) && $multicurrency != 2) { - $sql .= " AND cr.date_sync = (SELECT MAX(cr2.date_sync) FROM ".MAIN_DB_PREFIX."multicurrency_rate AS cr2 WHERE cr2.fk_multicurrency = m.rowid)"; - } - } - - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $list[] = $this->db->fetch_object($result); - } - } else { - throw new RestException(503, 'Error when retrieving list of currency : '.$this->db->lasterror()); - } - - return $list; - } - - /** - * Get the list of extra fields. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param string $type Type of element ('adherent', 'commande', 'thirdparty', 'facture', 'propal', 'product', ...) - * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.label:like:'SO-%')" - * @return array List of extra fields - * - * @url GET extrafields - * - * @throws RestException - */ - public function getListOfExtrafields($sortfield = "t.pos", $sortorder = 'ASC', $type = '', $sqlfilters = '') - { - $list = array(); - - if (!DolibarrApiAccess::$user->admin) { - throw new RestException(401, 'Only an admin user can get list of extrafields'); - } - - if ($type == 'thirdparty') { - $type = 'societe'; - } - if ($type == 'contact') { - $type = 'socpeople'; - } - - $sql = "SELECT t.rowid, t.name, t.label, t.type, t.size, t.elementtype, t.fieldunique, t.fieldrequired, t.param, t.pos, t.alwayseditable, t.perms, t.list, t.fielddefault, t.fieldcomputed"; - $sql .= " FROM ".MAIN_DB_PREFIX."extrafields as t"; - $sql .= " WHERE t.entity IN (".getEntity('extrafields').")"; - if (!empty($type)) { - $sql .= " AND t.elementtype = '".$this->db->escape($type)."'"; - } - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - $sql .= $this->db->order($sortfield, $sortorder); - - $resql = $this->db->query($sql); - if ($resql) { - if ($this->db->num_rows($resql)) { - while ($tab = $this->db->fetch_object($resql)) { - // New usage - $list[$tab->elementtype][$tab->name]['type'] = $tab->type; - $list[$tab->elementtype][$tab->name]['label'] = $tab->label; - $list[$tab->elementtype][$tab->name]['size'] = $tab->size; - $list[$tab->elementtype][$tab->name]['elementtype'] = $tab->elementtype; - $list[$tab->elementtype][$tab->name]['default'] = $tab->fielddefault; - $list[$tab->elementtype][$tab->name]['computed'] = $tab->fieldcomputed; - $list[$tab->elementtype][$tab->name]['unique'] = $tab->fieldunique; - $list[$tab->elementtype][$tab->name]['required'] = $tab->fieldrequired; - $list[$tab->elementtype][$tab->name]['param'] = ($tab->param ? jsonOrUnserialize($tab->param) : ''); // This may be a string encoded with serialise() or json_encode() - $list[$tab->elementtype][$tab->name]['pos'] = $tab->pos; - $list[$tab->elementtype][$tab->name]['alwayseditable'] = $tab->alwayseditable; - $list[$tab->elementtype][$tab->name]['perms'] = $tab->perms; - $list[$tab->elementtype][$tab->name]['list'] = $tab->list; - } - } - } else { - throw new RestException(503, 'Error when retrieving list of extra fields : '.$this->db->lasterror()); - } - - return $list; - } - - - /** - * Get the list of towns. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number (starting from zero) - * @param string $zipcode To filter on zipcode - * @param string $town To filter on city name - * @param int $active Town is active or not {@min 0} {@max 1} - * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" - * @return array List of towns - * - * @url GET dictionary/towns - * - * @throws RestException - */ - public function getListOfTowns($sortfield = "zip,town", $sortorder = 'ASC', $limit = 100, $page = 0, $zipcode = '', $town = '', $active = 1, $sqlfilters = '') - { - $list = array(); - - $sql = "SELECT rowid AS id, zip, town, fk_county, fk_pays AS fk_country"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_ziptown as t"; - $sql .= " WHERE t.active = ".((int) $active); - if ($zipcode) { - $sql .= " AND t.zip LIKE '%".$this->db->escape($zipcode)."%'"; - } - if ($town) { - $sql .= " AND t.town LIKE '%".$this->db->escape($town)."%'"; - } - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $list[] = $this->db->fetch_object($result); - } - } else { - throw new RestException(503, 'Error when retrieving list of towns : '.$this->db->lasterror()); - } - - return $list; - } - - /** - * Get the list of payments terms. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number {@min 0} - * @param int $active Payment term is active or not {@min 0} {@max 1} - * @param string $sqlfilters SQL criteria to filter. Syntax example "(t.code:=:'CHQ')" - * - * @url GET dictionary/payment_terms - * - * @return array List of payment terms - * - * @throws RestException 400 - */ - public function getPaymentTerms($sortfield = "sortorder", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '') - { - $list = array(); - - if (!DolibarrApiAccess::$user->hasRight('propal', 'lire') && !DolibarrApiAccess::$user->hasRight('commande', 'lire') && !DolibarrApiAccess::$user->hasRight('facture', 'lire')) { - throw new RestException(401); - } - - $sql = "SELECT rowid as id, code, sortorder, libelle as label, libelle_facture as descr, type_cdr, nbjour, decalage, module"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_payment_term as t"; - $sql .= " WHERE t.entity IN (".getEntity('c_payment_term').")"; - $sql .= " AND t.active = ".((int) $active); - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $list[] = $this->db->fetch_object($result); - } - } else { - throw new RestException(400, $this->db->lasterror()); - } - - return $list; - } - - /** - * Get the list of shipping methods. - * - * @param int $limit Number of items per page - * @param int $page Page number {@min 0} - * @param int $active Shipping methodsm is active or not {@min 0} {@max 1} - * @param string $lang Code of the language the label of the method must be translated to - * @param string $sqlfilters SQL criteria to filter. Syntax example "(t.code:=:'CHQ')" - * - * @url GET dictionary/shipping_methods - * - * @return array List of shipping methods - * - * @throws RestException 400 - */ - public function getShippingModes($limit = 100, $page = 0, $active = 1, $lang = '', $sqlfilters = '') - { - $list = array(); - - $sql = "SELECT rowid as id, code, libelle as label, description, tracking, module"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as t"; - $sql .= " WHERE t.entity IN (".getEntity('c_shipment_mode').")"; - $sql .= " AND t.active = ".((int) $active); - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - - //$sql.= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $method = $this->db->fetch_object($result); - $this->translateLabel($method, $lang, '', array('dict')); - $list[] = $method; - } - } else { - throw new RestException(400, $this->db->lasterror()); - } - - return $list; - } - - /** - * Get the list of measuring units. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number (starting from zero) - * @param int $active Measuring unit is active or not {@min 0} {@max 1} - * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" - * @return array List of measuring unit - * - * @url GET dictionary/units - * - * @throws RestException - */ - public function getListOfMeasuringUnits($sortfield = "rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '') - { - $list = array(); - - $sql = "SELECT t.rowid, t.code, t.label,t.short_label, t.active, t.scale, t.unit_type"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_units as t"; - $sql .= " WHERE t.active = ".((int) $active); - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $list[] = $this->db->fetch_object($result); - } - } else { - throw new RestException(503, 'Error when retrieving list of measuring units: '.$this->db->lasterror()); - } - - return $list; - } - - /** - * Get the list of legal form of business. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number (starting from zero) - * @param int $country To filter on country - * @param int $active Lega form is active or not {@min 0} {@max 1} - * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" - * @return array List of legal form - * - * @url GET dictionary/legal_form - * - * @throws RestException - */ - public function getListOfLegalForm($sortfield = "rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $country = 0, $active = 1, $sqlfilters = '') - { - $list = array(); - - $sql = "SELECT t.rowid, t.code, t.fk_pays, t.libelle, t.isvatexempted, t.active, t.module, t.position"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_forme_juridique as t"; - $sql .= " WHERE t.active = ".((int) $active); - if ($country) { - $sql .= " AND t.fk_pays = ".((int) $country); - } - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $list[] = $this->db->fetch_object($result); - } - } else { - throw new RestException(503, 'Error when retrieving list of legal form: '.$this->db->lasterror()); - } - - return $list; - } - - /** - * Get the list of staff. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number (starting from zero) - * @param int $active Staff is active or not {@min 0} {@max 1} - * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" - * @return array List of staff - * - * @url GET dictionary/staff - * - * @throws RestException - */ - public function getListOfStaff($sortfield = "id", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '') - { - $list = array(); - - $sql = "SELECT t.id, t.code, t.libelle, t.active, t.module"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_effectif as t"; - $sql .= " WHERE t.active = ".((int) $active); - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $list[] = $this->db->fetch_object($result); - } - } else { - throw new RestException(503, 'Error when retrieving list of staff: '.$this->db->lasterror()); - } - - return $list; - } - - /** - * Get the list of social networks. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number (starting from zero) - * @param int $active Social network is active or not {@min 0} {@max 1} - * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" - * @return array List of social networks - * - * @url GET dictionary/socialnetworks - * - * @throws RestException - */ - public function getListOfsocialNetworks($sortfield = "rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '') - { - global $conf; - - if (!isModEnabled('socialnetworks')) { - throw new RestException(400, 'API not available: this dictionary is not enabled by setup'); - } - - $list = array(); - //TODO link with multicurrency module - $sql = "SELECT t.rowid, t.entity, t.code, t.label, t.url, t.icon, t.active"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_socialnetworks as t"; - $sql .= " WHERE t.entity IN (".getEntity('c_socialnetworks').")"; - $sql .= " AND t.active = ".((int) $active); - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $list[] = $this->db->fetch_object($result); - } - } else { - throw new RestException(503, 'Error when retrieving list of social networks: '.$this->db->lasterror()); - } - - return $list; - } - - /** - * Get the list of tickets categories. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number (starting from zero) - * @param int $active Payment term is active or not {@min 0} {@max 1} - * @param string $lang Code of the language the label of the category must be translated to - * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" - * @return array List of ticket categories - * - * @url GET dictionary/ticket_categories - * - * @throws RestException - */ - public function getTicketsCategories($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $lang = '', $sqlfilters = '') - { - $list = array(); - - $sql = "SELECT rowid, code, pos, label, use_default, description"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_category as t"; - $sql .= " WHERE t.entity IN (".getEntity('c_ticket_category').")"; - $sql .= " AND t.active = ".((int) $active); - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $category = $this->db->fetch_object($result); - $this->translateLabel($category, $lang, 'TicketCategoryShort', array('ticket')); - $list[] = $category; - } - } else { - throw new RestException(503, 'Error when retrieving list of ticket categories : '.$this->db->lasterror()); - } - - return $list; - } - - /** - * Get the list of tickets severity. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number (starting from zero) - * @param int $active Payment term is active or not {@min 0} {@max 1} - * @param string $lang Code of the language the label of the severity must be translated to - * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" - * @return array List of ticket severities - * - * @url GET dictionary/ticket_severities - * - * @throws RestException - */ - public function getTicketsSeverities($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $lang = '', $sqlfilters = '') - { - $list = array(); - - $sql = "SELECT rowid, code, pos, label, use_default, color, description"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_severity as t"; - $sql .= " WHERE t.entity IN (".getEntity('c_ticket_severity').")"; - $sql .= " AND t.active = ".((int) $active); - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $severity = $this->db->fetch_object($result); - $this->translateLabel($severity, $lang, 'TicketSeverityShort', array('ticket')); - $list[] = $severity; - } - } else { - throw new RestException(503, 'Error when retrieving list of ticket severities : '.$this->db->lasterror()); - } - - return $list; - } - - /** - * Get the list of tickets types. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number (starting from zero) - * @param int $active Payment term is active or not {@min 0} {@max 1} - * @param string $lang Code of the language the label of the type must be translated to - * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" - * @return array List of ticket types - * - * @url GET dictionary/ticket_types - * - * @throws RestException - */ - public function getTicketsTypes($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $lang = '', $sqlfilters = '') - { - $list = array(); - - $sql = "SELECT rowid, code, pos, label, use_default, description"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_type as t"; - $sql .= " WHERE t.entity IN (".getEntity('c_ticket_type').")"; - $sql .= " AND t.active = ".((int) $active); - - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); - if ($errormessage) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - } - - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $type =$this->db->fetch_object($result); - $this->translateLabel($type, $lang, 'TicketTypeShort', array('ticket')); - $list[] = $type; - } - } else { - throw new RestException(503, 'Error when retrieving list of ticket types : '.$this->db->lasterror()); - } - - return $list; - } - - /** - * Get the list of incoterms. - * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Number of items per page - * @param int $page Page number (starting from zero) - * @param int $active Payment term is active or not {@min 0} {@max 1} - * @param string $lang Code of the language the label of the type must be translated to - * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" - * @return array List of incoterm types - * - * @url GET dictionary/incoterms - * - * @throws RestException - */ - public function getListOfIncoterms($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $lang = '', $sqlfilters = '') - { - $list = array(); - - $sql = "SELECT rowid, code, active"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_incoterms as t"; - $sql .= " WHERE 1=1"; - - // Add sql filters - if ($sqlfilters) { - $errormessage = ''; - if (!DolibarrApi::_checkFilters($sqlfilters, $errormessage)) { - throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage); - } - $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)'; - $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; - } - - - $sql .= $this->db->order($sortfield, $sortorder); - - if ($limit) { - if ($page < 0) { - $page = 0; - } - $offset = $limit * $page; - - $sql .= $this->db->plimit($limit, $offset); - } - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $type =$this->db->fetch_object($result); - $list[] = $type; - } - } else { - throw new RestException(503, 'Error when retrieving list of incoterm types : '.$this->db->lasterror()); - } - - return $list; - } - - /** - * Get properties of company - * - * @url GET /company - * - * @return array|mixed Mysoc object - * - * @throws RestException 403 Forbidden - */ - public function getCompany() - { - global $conf, $mysoc; - - if (!DolibarrApiAccess::$user->admin - && (!getDolGlobalString('API_LOGINS_ALLOWED_FOR_GET_COMPANY') || DolibarrApiAccess::$user->login != $conf->global->API_LOGINS_ALLOWED_FOR_GET_COMPANY)) { - throw new RestException(403, 'Error API open to admin users only or to the users with logins defined into constant API_LOGINS_ALLOWED_FOR_GET_COMPANY'); - } - - unset($mysoc->pays); - unset($mysoc->note); - unset($mysoc->nom); - - unset($mysoc->lines); - - unset($mysoc->effectif); - unset($mysoc->effectif_id); - unset($mysoc->forme_juridique_code); - unset($mysoc->forme_juridique); - unset($mysoc->mode_reglement_supplier_id); - unset($mysoc->cond_reglement_supplier_id); - unset($mysoc->transport_mode_supplier_id); - unset($mysoc->fk_prospectlevel); - - unset($mysoc->total_ht); - unset($mysoc->total_tva); - unset($mysoc->total_localtax1); - unset($mysoc->total_localtax2); - unset($mysoc->total_ttc); - - unset($mysoc->lastname); - unset($mysoc->firstname); - unset($mysoc->civility_id); - - unset($mysoc->client); - unset($mysoc->prospect); - unset($mysoc->fournisseur); - unset($mysoc->contact_id); - - unset($mysoc->fk_incoterms); - unset($mysoc->label_incoterms); - unset($mysoc->location_incoterms); - - return $this->_cleanObjectDatas($mysoc); - } - - /** - * Get the list of establishments. - * - * @return array List of establishments - * - * @url GET /establishments - * - * @throws RestException - */ - public function getEstablishments() - { - $list = array(); - - $limit = 0; - - $sql = "SELECT e.rowid, e.rowid as ref, e.label, e.address, e.zip, e.town, e.status"; - $sql .= " FROM ".MAIN_DB_PREFIX."establishment as e"; - $sql .= " WHERE e.entity IN (".getEntity('establishment').')'; - // if ($type) $sql .= " AND t.type LIKE '%".$this->db->escape($type)."%'"; - // if ($module) $sql .= " AND t.module LIKE '%".$this->db->escape($module)."%'"; - // Add sql filters - - $result = $this->db->query($sql); - - if ($result) { - $num = $this->db->num_rows($result); - $min = min($num, ($limit <= 0 ? $num : $limit)); - for ($i = 0; $i < $min; $i++) { - $list[] = $this->db->fetch_object($result); - } - } else { - throw new RestException(503, 'Error when retrieving list of establishments : '.$this->db->lasterror()); - } - - return $list; - } - - /** - * Get establishment by ID. - * - * @param int $id ID of establishment - * @return Object Object with cleaned properties - * - * @url GET establishments/{id} - * - * @throws RestException - */ - public function getEtablishmentByID($id) - { - $establishment = new Establishment($this->db); - - $result = $establishment->fetch($id); - if ($result < 0) { - throw new RestException(503, 'Error when retrieving establishment : '.$establishment->error); - } elseif ($result == 0) { - throw new RestException(404, 'Establishment not found'); - } - - return $this->_cleanObjectDatas($establishment); - } - - /** - * Get value of a setup variables - * - * Note that conf variables that stores security key or password hashes can't be loaded with API. - * - * @param string $constantname Name of conf variable to get - * @return string Data without useless information - * - * @url GET conf/{constantname} - * - * @throws RestException 403 Forbidden - * @throws RestException 404 Error Bad or unknown value for constantname - */ - public function getConf($constantname) - { - global $conf; - - if (!DolibarrApiAccess::$user->admin - && (!getDolGlobalString('API_LOGINS_ALLOWED_FOR_CONST_READ') || DolibarrApiAccess::$user->login != getDolGlobalString('API_LOGINS_ALLOWED_FOR_CONST_READ'))) { - throw new RestException(403, 'Error API open to admin users only or to the users with logins defined into constant API_LOGINS_ALLOWED_FOR_CONST_READ'); - } - - if (!preg_match('/^[a-zA-Z0-9_]+$/', $constantname) || !isset($conf->global->$constantname)) { - throw new RestException(404, 'Error Bad or unknown value for constantname'); - } - if (isASecretKey($constantname)) { - throw new RestException(403, 'Forbidden. This parameter cant be read with APIs'); - } - - return getDolGlobalString($constantname); - } - - /** - * Do a test of integrity for files and setup. - * - * @param string $target Can be 'local' or 'default' or Url of the signatures file to use for the test. Must be reachable by the tested Dolibarr. - * @return array Result of file and setup integrity check - * - * @url GET checkintegrity - * - * @throws RestException 403 Forbidden - * @throws RestException 404 Signature file not found - * @throws RestException 500 Technical error - * @throws RestException 503 Forbidden - */ - public function getCheckIntegrity($target) - { - global $langs, $conf; - - if (!DolibarrApiAccess::$user->admin - && (!getDolGlobalString('API_LOGINS_ALLOWED_FOR_INTEGRITY_CHECK') || DolibarrApiAccess::$user->login != $conf->global->API_LOGINS_ALLOWED_FOR_INTEGRITY_CHECK)) { - throw new RestException(403, 'Error API open to admin users only or to the users with logins defined into constant API_LOGINS_ALLOWED_FOR_INTEGRITY_CHECK'); - } - - require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; - require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php'; - - $langs->load("admin"); - - $outexpectedchecksum = ''; - $outcurrentchecksum = ''; - - // Modified or missing files - $file_list = array('missing' => array(), 'updated' => array()); - - // Local file to compare to - $xmlshortfile = dol_sanitizeFileName(GETPOST('xmlshortfile', 'alpha') ? GETPOST('xmlshortfile', 'alpha') : 'filelist-'.DOL_VERSION.(!getDolGlobalString('MAIN_FILECHECK_LOCAL_SUFFIX') ? '' : $conf->global->MAIN_FILECHECK_LOCAL_SUFFIX).'.xml'.(!getDolGlobalString('MAIN_FILECHECK_LOCAL_EXT') ? '' : $conf->global->MAIN_FILECHECK_LOCAL_EXT)); - $xmlfile = DOL_DOCUMENT_ROOT.'/install/'.$xmlshortfile; - // Remote file to compare to - $xmlremote = ($target == 'default' ? '' : $target); - if (empty($xmlremote) && getDolGlobalString('MAIN_FILECHECK_URL')) { - $xmlremote = $conf->global->MAIN_FILECHECK_URL; - } - $param = 'MAIN_FILECHECK_URL_'.DOL_VERSION; - if (empty($xmlremote) && getDolGlobalString($param)) { - $xmlremote = getDolGlobalString($param); - } - if (empty($xmlremote)) { - $xmlremote = 'https://www.dolibarr.org/files/stable/signatures/filelist-'.DOL_VERSION.'.xml'; - } - if ($xmlremote && !preg_match('/^https?:\/\//i', $xmlremote)) { - $langs->load("errors"); - throw new RestException(500, $langs->trans("ErrorURLMustStartWithHttp", $xmlremote)); - } - if ($xmlremote && !preg_match('/\.xml$/', $xmlremote)) { - $langs->load("errors"); - throw new RestException(500, $langs->trans("ErrorURLMustEndWith", $xmlremote, '.xml')); - } - - if (LIBXML_VERSION < 20900) { - // Avoid load of external entities (security problem). - // Required only if LIBXML_VERSION < 20900 - libxml_disable_entity_loader(true); - } - - if ($target == 'local') { - if (dol_is_file($xmlfile)) { - $xml = simplexml_load_file($xmlfile); - } else { - throw new RestException(500, $langs->trans('XmlNotFound').': '.$xmlfile); - } - } else { - $xmlarray = getURLContent($xmlremote, 'GET', '', 1, array(), array('http', 'https'), 0); // Accept http or https links on external remote server only. Same is used into filecheck.php. - - // Return array('content'=>response,'curl_error_no'=>errno,'curl_error_msg'=>errmsg...) - if (!$xmlarray['curl_error_no'] && $xmlarray['http_code'] != '400' && $xmlarray['http_code'] != '404') { - $xmlfile = $xmlarray['content']; - //print "xmlfilestart".$xmlfile."endxmlfile"; - $xml = simplexml_load_string($xmlfile, 'SimpleXMLElement', LIBXML_NOCDATA|LIBXML_NONET); - } else { - $errormsg = $langs->trans('XmlNotFound').': '.$xmlremote.' - '.$xmlarray['http_code'].(($xmlarray['http_code'] == 400 && $xmlarray['content']) ? ' '.$xmlarray['content'] : '').' '.$xmlarray['curl_error_no'].' '.$xmlarray['curl_error_msg']; - throw new RestException(500, $errormsg); - } - } - - if ($xml) { - $checksumconcat = array(); - $file_list = array(); - $out = ''; - - // Forced constants - if (is_object($xml->dolibarr_constants[0])) { - $out .= load_fiche_titre($langs->trans("ForcedConstants")); - - $out .= '
'; - $out .= ''; - $out .= ''; - $out .= ''; - $out .= ''; - $out .= ''; - $out .= ''; - $out .= ''."\n"; - - $i = 0; - foreach ($xml->dolibarr_constants[0]->constant as $constant) { // $constant is a simpleXMLElement - $constname = $constant['name']; - $constvalue = (string) $constant; - $constvalue = (empty($constvalue) ? '0' : $constvalue); - // Value found - $value = ''; - if ($constname && getDolGlobalString($constname) != '') { - $value = getDolGlobalString($constname); - } - $valueforchecksum = (empty($value) ? '0' : $value); - - $checksumconcat[] = $valueforchecksum; - - $i++; - $out .= ''; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= "\n"; - } - - if ($i == 0) { - $out .= ''; - } - $out .= '
#'.$langs->trans("Constant").''.$langs->trans("ExpectedValue").''.$langs->trans("Value").'
'.$i.''.$constname.''.$constvalue.''.$valueforchecksum.'
'.$langs->trans("None").'
'; - $out .= '
'; - - $out .= '
'; - } - - // Scan htdocs - if (is_object($xml->dolibarr_htdocs_dir[0])) { - $includecustom = (empty($xml->dolibarr_htdocs_dir[0]['includecustom']) ? 0 : $xml->dolibarr_htdocs_dir[0]['includecustom']); - - // Define qualified files (must be same than into generate_filelist_xml.php and in api_setup.class.php) - $regextoinclude = '\.(php|php3|php4|php5|phtml|phps|phar|inc|css|scss|html|xml|js|json|tpl|jpg|jpeg|png|gif|ico|sql|lang|txt|yml|bak|md|mp3|mp4|wav|mkv|z|gz|zip|rar|tar|less|svg|eot|woff|woff2|ttf|manifest)$'; - $regextoexclude = '('.($includecustom ? '' : 'custom|').'documents|conf|install|dejavu-fonts-ttf-.*|public\/test|sabre\/sabre\/.*\/tests|Shared\/PCLZip|nusoap\/lib\/Mail|php\/example|php\/test|geoip\/sample.*\.php|ckeditor\/samples|ckeditor\/adapters)$'; // Exclude dirs - $scanfiles = dol_dir_list(DOL_DOCUMENT_ROOT, 'files', 1, $regextoinclude, $regextoexclude); - - // Fill file_list with files in signature, new files, modified files - $ret = getFilesUpdated($file_list, $xml->dolibarr_htdocs_dir[0], '', DOL_DOCUMENT_ROOT, $checksumconcat); // Fill array $file_list - // Complete with list of new files - foreach ($scanfiles as $keyfile => $valfile) { - $tmprelativefilename = preg_replace('/^'.preg_quote(DOL_DOCUMENT_ROOT, '/').'/', '', $valfile['fullname']); - if (!in_array($tmprelativefilename, $file_list['insignature'])) { - $md5newfile = @md5_file($valfile['fullname']); // Can fails if we don't have permission to open/read file - $file_list['added'][] = array('filename'=>$tmprelativefilename, 'md5'=>$md5newfile); - } - } - - // Files missings - $out .= load_fiche_titre($langs->trans("FilesMissing")); - - $out .= '
'; - $out .= ''; - $out .= ''; - $out .= ''; - $out .= ''; - $out .= ''; - $out .= ''."\n"; - $tmpfilelist = dol_sort_array($file_list['missing'], 'filename'); - if (is_array($tmpfilelist) && count($tmpfilelist)) { - $i = 0; - foreach ($tmpfilelist as $file) { - $i++; - $out .= ''; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= "\n"; - } - } else { - $out .= ''; - } - $out .= '
#'.$langs->trans("Filename").''.$langs->trans("ExpectedChecksum").'
'.$i.''.$file['filename'].''.$file['expectedmd5'].'
'.$langs->trans("None").'
'; - $out .= '
'; - - $out .= '
'; - - // Files modified - $out .= load_fiche_titre($langs->trans("FilesModified")); - - $totalsize = 0; - $out .= '
'; - $out .= ''; - $out .= ''; - $out .= ''; - $out .= ''; - $out .= ''; - $out .= ''; - $out .= ''; - $out .= ''; - $out .= ''."\n"; - $tmpfilelist2 = dol_sort_array($file_list['updated'], 'filename'); - if (is_array($tmpfilelist2) && count($tmpfilelist2)) { - $i = 0; - foreach ($tmpfilelist2 as $file) { - $i++; - $out .= ''; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= ''."\n"; - $size = dol_filesize(DOL_DOCUMENT_ROOT.'/'.$file['filename']); - $totalsize += $size; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= "\n"; - } - $out .= ''; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= "\n"; - } else { - $out .= ''; - } - $out .= '
#'.$langs->trans("Filename").''.$langs->trans("ExpectedChecksum").''.$langs->trans("CurrentChecksum").''.$langs->trans("Size").''.$langs->trans("DateModification").'
'.$i.''.$file['filename'].''.$file['expectedmd5'].''.$file['md5'].''.dol_print_size($size).''.dol_print_date(dol_filemtime(DOL_DOCUMENT_ROOT.'/'.$file['filename']), 'dayhour').'
'.$langs->trans("Total").''.dol_print_size($totalsize).'
'.$langs->trans("None").'
'; - $out .= '
'; - - $out .= '
'; - - // Files added - $out .= load_fiche_titre($langs->trans("FilesAdded")); - - $totalsize = 0; - $out .= '
'; - $out .= ''; - $out .= ''; - $out .= ''; - $out .= ''; - $out .= ''; - $out .= ''; - $out .= ''; - $out .= ''; - $out .= ''."\n"; - $tmpfilelist3 = dol_sort_array($file_list['added'], 'filename'); - if (is_array($tmpfilelist3) && count($tmpfilelist3)) { - $i = 0; - foreach ($tmpfilelist3 as $file) { - $i++; - $out .= ''; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= ''."\n"; - $size = dol_filesize(DOL_DOCUMENT_ROOT.'/'.$file['filename']); - $totalsize += $size; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= "\n"; - } - $out .= ''; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= "\n"; - } else { - $out .= ''; - } - $out .= '
#'.$langs->trans("Filename").''.$langs->trans("ExpectedChecksum").''.$langs->trans("CurrentChecksum").''.$langs->trans("Size").''.$langs->trans("DateModification").'
'.$i.''.$file['filename'].''.$file['expectedmd5'].''.$file['md5'].''.dol_print_size($size).''.dol_print_date(dol_filemtime(DOL_DOCUMENT_ROOT.'/'.$file['filename']), 'dayhour').'
'.$langs->trans("Total").''.dol_print_size($totalsize).'
'.$langs->trans("None").'
'; - $out .= '
'; - - - // Show warning - if (empty($tmpfilelist) && empty($tmpfilelist2) && empty($tmpfilelist3)) { - //setEventMessages($langs->trans("FileIntegrityIsStrictlyConformedWithReference"), null, 'mesgs'); - } else { - //setEventMessages($langs->trans("FileIntegritySomeFilesWereRemovedOrModified"), null, 'warnings'); - } - } else { - throw new RestException(500, 'Error: Failed to found dolibarr_htdocs_dir into XML file '.$xmlfile); - } - - - // Scan scripts - asort($checksumconcat); // Sort list of checksum - $checksumget = md5(join(',', $checksumconcat)); - $checksumtoget = trim((string) $xml->dolibarr_htdocs_dir_checksum); - - $outexpectedchecksum = ($checksumtoget ? $checksumtoget : $langs->trans("Unknown")); - if ($checksumget == $checksumtoget) { - if (count($file_list['added'])) { - $resultcode = 'warning'; - $resultcomment = 'FileIntegrityIsOkButFilesWereAdded'; - //$outcurrentchecksum = $checksumget.' - '.$langs->trans("FileIntegrityIsOkButFilesWereAdded").''; - $outcurrentchecksum = $checksumget; - } else { - $resultcode = 'ok'; - $resultcomment = 'Success'; - //$outcurrentchecksum = ''.$checksumget.''; - $outcurrentchecksum = $checksumget; - } - } else { - $resultcode = 'error'; - $resultcomment = 'Error'; - //$outcurrentchecksum = ''.$checksumget.''; - $outcurrentchecksum = $checksumget; - } - } else { - throw new RestException(404, 'No signature file known'); - } - - return array('resultcode'=>$resultcode, 'resultcomment'=>$resultcomment, 'expectedchecksum'=> $outexpectedchecksum, 'currentchecksum'=> $outcurrentchecksum, 'out'=>$out); - } - - - /** - * Get list of enabled modules - * - * @url GET /modules - * - * @return array|mixed Data without useless information - * - * @throws RestException 403 Forbidden - */ - public function getModules() - { - global $conf; - - if (!DolibarrApiAccess::$user->admin - && (!getDolGlobalString('API_LOGINS_ALLOWED_FOR_GET_MODULES') || DolibarrApiAccess::$user->login != $conf->global->API_LOGINS_ALLOWED_FOR_GET_MODULES)) { - throw new RestException(403, 'Error API open to admin users only or to the users with logins defined into constant API_LOGINS_ALLOWED_FOR_GET_MODULES'); - } - - sort($conf->modules); - - return $this->_cleanObjectDatas($conf->modules); - } + //TODO link with multicurrency module + $sql = "SELECT t.rowid, t.entity, t.code, t.label, t.url, t.icon, t.active"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_socialnetworks as t"; + $sql .= " WHERE t.entity IN (".getEntity('c_socialnetworks').")"; + $sql .= " AND t.active = ".$active; + // Add sql filters + if ($sqlfilters) + { + if (!DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + + $sql .= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $list[] = $this->db->fetch_object($result); + } + } else { + throw new RestException(503, 'Error when retrieving list of social networks: '.$this->db->lasterror()); + } + + return $list; + } + + /** + * Get the list of tickets categories. + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Number of items per page + * @param int $page Page number (starting from zero) + * @param int $active Payment term is active or not {@min 0} {@max 1} + * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" + * @return array List of ticket categories + * + * @url GET dictionary/ticket_categories + * + * @throws RestException + */ + public function getTicketsCategories($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '') + { + $list = array(); + + $sql = "SELECT rowid, code, pos, label, use_default, description"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_category as t"; + $sql .= " WHERE t.active = ".$active; + // Add sql filters + if ($sqlfilters) + { + if (!DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + + $sql .= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $list[] = $this->db->fetch_object($result); + } + } else { + throw new RestException(503, 'Error when retrieving list of ticket categories : '.$this->db->lasterror()); + } + + return $list; + } + + /** + * Get the list of tickets severity. + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Number of items per page + * @param int $page Page number (starting from zero) + * @param int $active Payment term is active or not {@min 0} {@max 1} + * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" + * @return array List of ticket severities + * + * @url GET dictionary/ticket_severities + * + * @throws RestException + */ + public function getTicketsSeverities($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '') + { + $list = array(); + + $sql = "SELECT rowid, code, pos, label, use_default, color, description"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_severity as t"; + $sql .= " WHERE t.active = ".$active; + // Add sql filters + if ($sqlfilters) + { + if (!DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + + $sql .= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $list[] = $this->db->fetch_object($result); + } + } else { + throw new RestException(503, 'Error when retrieving list of ticket severities : '.$this->db->lasterror()); + } + + return $list; + } + + /** + * Get the list of tickets types. + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Number of items per page + * @param int $page Page number (starting from zero) + * @param int $active Payment term is active or not {@min 0} {@max 1} + * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" + * @return array List of ticket types + * + * @url GET dictionary/ticket_types + * + * @throws RestException + */ + public function getTicketsTypes($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '') + { + $list = array(); + + $sql = "SELECT rowid, code, pos, label, use_default, description"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_type as t"; + $sql .= " WHERE t.active = ".(int) $active; + // if ($type) $sql .= " AND t.type LIKE '%".$this->db->escape($type)."%'"; + // if ($module) $sql .= " AND t.module LIKE '%".$this->db->escape($module)."%'"; + // Add sql filters + if ($sqlfilters) + { + if (!DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + + $sql .= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $list[] = $this->db->fetch_object($result); + } + } else { + throw new RestException(503, 'Error when retrieving list of ticket types : '.$this->db->lasterror()); + } + + return $list; + } + + /** + * Get properties of company + * + * @url GET /company + * + * @return array|mixed Data without useless information + * + */ + public function getCompany() + { + global $mysoc; + + return $this->_cleanObjectDatas($mysoc); + } + + + /** + * Get value of a setup variables + * + * Note that conf variables that stores security key or password hashes can't be loaded with API. + * + * @param string $constantname Name of conf variable to get + * @return array|mixed Data without useless information + * + * @url GET conf/{constantname} + * + * @throws RestException 403 Forbidden + * @throws RestException 500 Error Bad or unknown value for constantname + */ + public function getConf($constantname) + { + global $conf; + + if (!DolibarrApiAccess::$user->admin + && (empty($conf->global->API_LOGIN_ALLOWED_FOR_ADMIN_CHECK) || DolibarrApiAccess::$user->login != $conf->global->API_LOGIN_ALLOWED_FOR_ADMIN_CHECK)) { + throw new RestException(403, 'Error API open to admin users only or to the login user defined with constant API_LOGIN_ALLOWED_FOR_ADMIN_CHECK'); + } + + if (!preg_match('/^[a-zA-Z0-9_]+$/', $constantname) || !isset($conf->global->$constantname)) { + throw new RestException(500, 'Error Bad or unknown value for constantname'); + } + if (preg_match('/(_pass|_pw|password|secret|_key|key$)/i', $constantname)) { + throw new RestException(403, 'Forbidden'); + } + + return $conf->global->$constantname; + } + + /** + * Do a test of integrity for files and setup. + * + * @param string $target Can be 'local' or 'default' or Url of the signatures file to use for the test. Must be reachable by the tested Dolibarr. + * @return array Result of file and setup integrity check + * + * @url GET checkintegrity + * + * @throws RestException 404 Signature file not found + * @throws RestException 500 Technical error + * @throws RestException 503 Forbidden + */ + public function getCheckIntegrity($target) + { + global $langs, $conf; + + if (!DolibarrApiAccess::$user->admin + && (empty($conf->global->API_LOGIN_ALLOWED_FOR_INTEGRITY_CHECK) || DolibarrApiAccess::$user->login != $conf->global->API_LOGIN_ALLOWED_FOR_INTEGRITY_CHECK)) + { + throw new RestException(503, 'Error API open to admin users only or to the login user defined with constant API_LOGIN_ALLOWED_FOR_INTEGRITY_CHECK'); + } + + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php'; + + $langs->load("admin"); + + $outexpectedchecksum = ''; + $outcurrentchecksum = ''; + + // Modified or missing files + $file_list = array('missing' => array(), 'updated' => array()); + + // Local file to compare to + $xmlshortfile = GETPOST('xmlshortfile') ?GETPOST('xmlshortfile') : '/install/filelist-'.DOL_VERSION.'.xml'; + $xmlfile = DOL_DOCUMENT_ROOT.$xmlshortfile; + // Remote file to compare to + $xmlremote = ($target == 'default' ? '' : $target); + if (empty($xmlremote) && !empty($conf->global->MAIN_FILECHECK_URL)) $xmlremote = $conf->global->MAIN_FILECHECK_URL; + $param = 'MAIN_FILECHECK_URL_'.DOL_VERSION; + if (empty($xmlremote) && !empty($conf->global->$param)) $xmlremote = $conf->global->$param; + if (empty($xmlremote)) $xmlremote = 'https://www.dolibarr.org/files/stable/signatures/filelist-'.DOL_VERSION.'.xml'; + + if ($target == 'local') + { + if (dol_is_file($xmlfile)) + { + $xml = simplexml_load_file($xmlfile); + } + else + { + throw new RestException(500, $langs->trans('XmlNotFound').': '.$xmlfile); + } + } + else + { + $xmlarray = getURLContent($xmlremote); + + // Return array('content'=>response,'curl_error_no'=>errno,'curl_error_msg'=>errmsg...) + if (!$xmlarray['curl_error_no'] && $xmlarray['http_code'] != '404') + { + $xmlfile = $xmlarray['content']; + //print "xmlfilestart".$xmlfile."endxmlfile"; + $xml = simplexml_load_string($xmlfile); + } + else + { + $errormsg = $langs->trans('XmlNotFound').': '.$xmlremote.' - '.$xmlarray['http_code'].' '.$xmlarray['curl_error_no'].' '.$xmlarray['curl_error_msg']; + throw new RestException(500, $errormsg); + } + } + + + + if ($xml) + { + $checksumconcat = array(); + $file_list = array(); + $out = ''; + + // Forced constants + if (is_object($xml->dolibarr_constants[0])) + { + $out .= load_fiche_titre($langs->trans("ForcedConstants")); + + $out .= '
'; + $out .= ''; + $out .= ''; + $out .= ''; + $out .= ''; + $out .= ''; + $out .= ''; + $out .= ''."\n"; + + $i = 0; + foreach ($xml->dolibarr_constants[0]->constant as $constant) // $constant is a simpleXMLElement + { + $constname = $constant['name']; + $constvalue = (string) $constant; + $constvalue = (empty($constvalue) ? '0' : $constvalue); + // Value found + $value = ''; + if ($constname && $conf->global->$constname != '') $value = $conf->global->$constname; + $valueforchecksum = (empty($value) ? '0' : $value); + + $checksumconcat[] = $valueforchecksum; + + $i++; + $out .= ''; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= "\n"; + } + + if ($i == 0) + { + $out .= ''; + } + $out .= '
#'.$langs->trans("Constant").''.$langs->trans("ExpectedValue").''.$langs->trans("Value").'
'.$i.''.$constname.''.$constvalue.''.$valueforchecksum.'
'.$langs->trans("None").'
'; + $out .= '
'; + + $out .= '
'; + } + + // Scan htdocs + if (is_object($xml->dolibarr_htdocs_dir[0])) + { + //var_dump($xml->dolibarr_htdocs_dir[0]['includecustom']);exit; + $includecustom = (empty($xml->dolibarr_htdocs_dir[0]['includecustom']) ? 0 : $xml->dolibarr_htdocs_dir[0]['includecustom']); + + // Defined qualified files (must be same than into generate_filelist_xml.php) + $regextoinclude = '\.(php|php3|php4|php5|phtml|phps|phar|inc|css|scss|html|xml|js|json|tpl|jpg|jpeg|png|gif|ico|sql|lang|txt|yml|bak|md|mp3|mp4|wav|mkv|z|gz|zip|rar|tar|less|svg|eot|woff|woff2|ttf|manifest)$'; + $regextoexclude = '('.($includecustom ? '' : 'custom|').'documents|conf|install|public\/test|Shared\/PCLZip|nusoap\/lib\/Mail|php\/example|php\/test|geoip\/sample.*\.php|ckeditor\/samples|ckeditor\/adapters)$'; // Exclude dirs + $scanfiles = dol_dir_list(DOL_DOCUMENT_ROOT, 'files', 1, $regextoinclude, $regextoexclude); + + // Fill file_list with files in signature, new files, modified files + $ret = getFilesUpdated($file_list, $xml->dolibarr_htdocs_dir[0], '', DOL_DOCUMENT_ROOT, $checksumconcat); // Fill array $file_list + // Complete with list of new files + foreach ($scanfiles as $keyfile => $valfile) + { + $tmprelativefilename = preg_replace('/^'.preg_quote(DOL_DOCUMENT_ROOT, '/').'/', '', $valfile['fullname']); + if (!in_array($tmprelativefilename, $file_list['insignature'])) + { + $md5newfile = @md5_file($valfile['fullname']); // Can fails if we don't have permission to open/read file + $file_list['added'][] = array('filename'=>$tmprelativefilename, 'md5'=>$md5newfile); + } + } + + // Files missings + $out .= load_fiche_titre($langs->trans("FilesMissing")); + + $out .= '
'; + $out .= ''; + $out .= ''; + $out .= ''; + $out .= ''; + $out .= ''; + $out .= ''."\n"; + $tmpfilelist = dol_sort_array($file_list['missing'], 'filename'); + if (is_array($tmpfilelist) && count($tmpfilelist)) + { + $i = 0; + foreach ($tmpfilelist as $file) + { + $i++; + $out .= ''; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= "\n"; + } + } + else + { + $out .= ''; + } + $out .= '
#'.$langs->trans("Filename").''.$langs->trans("ExpectedChecksum").'
'.$i.''.$file['filename'].''.$file['expectedmd5'].'
'.$langs->trans("None").'
'; + $out .= '
'; + + $out .= '
'; + + // Files modified + $out .= load_fiche_titre($langs->trans("FilesModified")); + + $totalsize = 0; + $out .= '
'; + $out .= ''; + $out .= ''; + $out .= ''; + $out .= ''; + $out .= ''; + $out .= ''; + $out .= ''; + $out .= ''; + $out .= ''."\n"; + $tmpfilelist2 = dol_sort_array($file_list['updated'], 'filename'); + if (is_array($tmpfilelist2) && count($tmpfilelist2)) + { + $i = 0; + foreach ($tmpfilelist2 as $file) + { + $i++; + $out .= ''; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= ''."\n"; + $size = dol_filesize(DOL_DOCUMENT_ROOT.'/'.$file['filename']); + $totalsize += $size; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= "\n"; + } + $out .= ''; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= "\n"; + } + else + { + $out .= ''; + } + $out .= '
#'.$langs->trans("Filename").''.$langs->trans("ExpectedChecksum").''.$langs->trans("CurrentChecksum").''.$langs->trans("Size").''.$langs->trans("DateModification").'
'.$i.''.$file['filename'].''.$file['expectedmd5'].''.$file['md5'].''.dol_print_size($size).''.dol_print_date(dol_filemtime(DOL_DOCUMENT_ROOT.'/'.$file['filename']), 'dayhour').'
'.$langs->trans("Total").''.dol_print_size($totalsize).'
'.$langs->trans("None").'
'; + $out .= '
'; + + $out .= '
'; + + // Files added + $out .= load_fiche_titre($langs->trans("FilesAdded")); + + $totalsize = 0; + $out .= '
'; + $out .= ''; + $out .= ''; + $out .= ''; + $out .= ''; + $out .= ''; + $out .= ''; + $out .= ''; + $out .= ''; + $out .= ''."\n"; + $tmpfilelist3 = dol_sort_array($file_list['added'], 'filename'); + if (is_array($tmpfilelist3) && count($tmpfilelist3)) + { + $i = 0; + foreach ($tmpfilelist3 as $file) + { + $i++; + $out .= ''; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= ''."\n"; + $size = dol_filesize(DOL_DOCUMENT_ROOT.'/'.$file['filename']); + $totalsize += $size; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= "\n"; + } + $out .= ''; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= "\n"; + } + else + { + $out .= ''; + } + $out .= '
#'.$langs->trans("Filename").''.$langs->trans("ExpectedChecksum").''.$langs->trans("CurrentChecksum").''.$langs->trans("Size").''.$langs->trans("DateModification").'
'.$i.''.$file['filename'].''.$file['expectedmd5'].''.$file['md5'].''.dol_print_size($size).''.dol_print_date(dol_filemtime(DOL_DOCUMENT_ROOT.'/'.$file['filename']), 'dayhour').'
'.$langs->trans("Total").''.dol_print_size($totalsize).'
'.$langs->trans("None").'
'; + $out .= '
'; + + + // Show warning + if (empty($tmpfilelist) && empty($tmpfilelist2) && empty($tmpfilelist3)) + { + //setEventMessages($langs->trans("FileIntegrityIsStrictlyConformedWithReference"), null, 'mesgs'); + } + else + { + //setEventMessages($langs->trans("FileIntegritySomeFilesWereRemovedOrModified"), null, 'warnings'); + } + } + else + { + throw new RestException(500, 'Error: Failed to found dolibarr_htdocs_dir into XML file '.$xmlfile); + } + + + // Scan scripts + + + asort($checksumconcat); // Sort list of checksum + //var_dump($checksumconcat); + $checksumget = md5(join(',', $checksumconcat)); + $checksumtoget = trim((string) $xml->dolibarr_htdocs_dir_checksum); + + $outexpectedchecksum = ($checksumtoget ? $checksumtoget : $langs->trans("Unknown")); + if ($checksumget == $checksumtoget) + { + if (count($file_list['added'])) + { + $resultcode = 'warning'; + $resultcomment = 'FileIntegrityIsOkButFilesWereAdded'; + //$outcurrentchecksum = $checksumget.' - '.$langs->trans("FileIntegrityIsOkButFilesWereAdded").''; + $outcurrentchecksum = $checksumget; + } + else + { + $resultcode = 'ok'; + $resultcomment = 'Success'; + //$outcurrentchecksum = ''.$checksumget.''; + $outcurrentchecksum = $checksumget; + } + } + else + { + $resultcode = 'error'; + $resultcomment = 'Error'; + //$outcurrentchecksum = ''.$checksumget.''; + $outcurrentchecksum = $checksumget; + } + } + else { + throw new RestException(404, 'No signature file known'); + } + + return array('resultcode'=>$resultcode, 'resultcomment'=>$resultcomment, 'expectedchecksum'=> $outexpectedchecksum, 'currentchecksum'=> $outcurrentchecksum, 'out'=>$out); + } --- /tmp/dsg/dolibarr/htdocs/api/class/github_19.0.3_api_status.class.php +++ /tmp/dsg/dolibarr/htdocs/api/class/client_api_status.class.php @@ -18 +17,0 @@ -require_once DOL_DOCUMENT_ROOT.'/api/class/api.class.php'; @@ -28 +27 @@ -class Status extends DolibarrApi +class Status @@ -30,8 +29,8 @@ - /** - * Constructor of the class - */ - public function __construct() - { - global $db; - $this->db = $db; - } + /** + * Get status (Dolibarr version) + * + * @return array + */ + public function index() + { + global $conf; @@ -39,17 +38,8 @@ - /** - * Get status (Dolibarr version) - * - * @return array - */ - public function index() - { - global $conf; - - return array( - 'success' => array( - 'code' => 200, - 'dolibarr_version' => DOL_VERSION, - 'access_locked' => (!getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED') ? '0' : $conf->global->MAIN_ONLY_LOGIN_ALLOWED), - ), - ); - } + return array( + 'success' => array( + 'code' => 200, + 'dolibarr_version' => DOL_VERSION, + 'access_locked' => (empty($conf->global->MAIN_ONLY_LOGIN_ALLOWED) ? '0' : $conf->global->MAIN_ONLY_LOGIN_ALLOWED), + ), + ); + }