Setup::getCheckIntegrity()   F
last analyzed

Complexity

Conditions 53
Paths > 20000

Size

Total Lines 308
Code Lines 217

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 53
eloc 217
nc 886529
nop 1
dl 0
loc 308
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/* Copyright (C) 2016       Xebax Christy               <[email protected]>
4
 * Copyright (C) 2016	    Laurent Destailleur		    <[email protected]>
5
 * Copyright (C) 2017	    Regis Houssin	            <[email protected]>
6
 * Copyright (C) 2017	    Neil Orley	                <[email protected]>
7
 * Copyright (C) 2018-2021  Frédéric France             <[email protected]>
8
 * Copyright (C) 2018-2022  Thibault FOUCART            <[email protected]>
9
 * Copyright (C) 2024       Jon Bendtsen                <[email protected]>
10
 * Copyright (C) 2024       Rafael San José             <[email protected]>
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 3 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
24
 */
25
26
namespace Dolibarr\Code\Api\Api;
27
28
use Dolibarr\Code\Api\Classes\DolibarrApiAccess;
29
use Dolibarr\Code\Core\Classes\Ccountry;
30
use Dolibarr\Code\Core\Classes\Cregion;
31
use Dolibarr\Code\Core\Classes\Cstate;
32
use Dolibarr\Code\Core\Classes\ExtraFields;
33
use Dolibarr\Code\Core\Classes\Translate;
34
use Dolibarr\Code\Hrm\Classes\Establishment;
35
use Dolibarr\Core\Base\DolibarrApi;
36
use Luracast\Restler\RestException;
37
38
require_once constant('DOL_DOCUMENT_ROOT') . '/main.inc.php';
39
40
/**
41
 * API class for dictionaries
42
 *
43
 * @access protected
44
 * @class DolibarrApiAccess {@requires user,external}
45
 */
46
class Setup extends DolibarrApi
47
{
48
    private $translations = null;
49
50
    /**
51
     * Constructor
52
     */
53
    public function __construct()
54
    {
55
        global $db;
56
        $this->db = $db;
57
    }
58
59
    /**
60
     * Get the list of ordering methods.
61
     *
62
     * @param string    $sortfield  Sort field
63
     * @param string    $sortorder  Sort order
64
     * @param int       $limit      Number of items per page
65
     * @param int       $page       Page number {@min 0}
66
     * @param int       $active     Payment type is active or not {@min 0} {@max 1}
67
     * @param string    $sqlfilters SQL criteria to filter with. Syntax example "(t.code:=:'OrderByWWW')"
68
     *
69
     * @url     GET dictionary/ordering_methods
70
     *
71
     * @return array [List of ordering methods]
72
     *
73
     * @throws  RestException   400     Bad value for sqlfilters
74
     * @throws  RestException   403     Access denied
75
     * @throws  RestException   503     Error retrieving list of ordering methods
76
     */
77
    public function getOrderingMethods($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '')
78
    {
79
        $list = array();
80
81
        if (!DolibarrApiAccess::$user->hasRight('commande', 'lire')) {
82
            throw new RestException(403);
83
        }
84
85
        $sql = "SELECT rowid, code, libelle as label, module";
86
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_input_method as t";
87
        $sql .= " WHERE t.active = " . ((int) $active);
88
        // Add sql filters
89
        if ($sqlfilters) {
90
            $errormessage = '';
91
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
92
            if ($errormessage) {
93
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
94
            }
95
        }
96
97
98
        $sql .= $this->db->order($sortfield, $sortorder);
99
100
        if ($limit) {
101
            if ($page < 0) {
102
                $page = 0;
103
            }
104
            $offset = $limit * $page;
105
106
            $sql .= $this->db->plimit($limit, $offset);
107
        }
108
109
        $result = $this->db->query($sql);
110
111
        if ($result) {
112
            $num = $this->db->num_rows($result);
113
            $min = min($num, ($limit <= 0 ? $num : $limit));
114
            for ($i = 0; $i < $min; $i++) {
115
                $list[] = $this->db->fetch_object($result);
116
            }
117
        } else {
118
            throw new RestException(503, $this->db->lasterror());
119
        }
120
121
        return $list;
122
    }
123
124
    /**
125
     * Get the list of ordering origins.
126
     *
127
     * @param string    $sortfield  Sort field
128
     * @param string    $sortorder  Sort order
129
     * @param int       $limit      Number of items per page
130
     * @param int       $page       Page number {@min 0}
131
     * @param int       $active     Payment type is active or not {@min 0} {@max 1}
132
     * @param string    $sqlfilters SQL criteria to filter with. Syntax example "(t.code:=:'OrderByWWW')"
133
     * @return array [List of ordering reasons]
134
     *
135
     * @url     GET dictionary/ordering_origins
136
     *
137
     * @throws  RestException   400     Bad value for sqlfilters
138
     * @throws  RestException   403     Access denied
139
     * @throws  RestException   503     Error retrieving list of ordering origins
140
     */
141
    public function getOrderingOrigins($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '')
142
    {
143
        $list = array();
144
145
        if (!DolibarrApiAccess::$user->hasRight('commande', 'lire')) {
146
            throw new RestException(403);
147
        }
148
149
        $sql = "SELECT rowid, code, label, module";
150
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_input_reason as t";
151
        $sql .= " WHERE t.active = " . ((int) $active);
152
        // Add sql filters
153
        if ($sqlfilters) {
154
            $errormessage = '';
155
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
156
            if ($errormessage) {
157
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
158
            }
159
        }
160
161
162
        $sql .= $this->db->order($sortfield, $sortorder);
163
164
        if ($limit) {
165
            if ($page < 0) {
166
                $page = 0;
167
            }
168
            $offset = $limit * $page;
169
170
            $sql .= $this->db->plimit($limit, $offset);
171
        }
172
173
        $result = $this->db->query($sql);
174
175
        if ($result) {
176
            $num = $this->db->num_rows($result);
177
            $min = min($num, ($limit <= 0 ? $num : $limit));
178
            for ($i = 0; $i < $min; $i++) {
179
                $list[] = $this->db->fetch_object($result);
180
            }
181
        } else {
182
            throw new RestException(503, $this->db->lasterror());
183
        }
184
185
        return $list;
186
    }
187
188
    /**
189
     * Get the list of payments types.
190
     *
191
     * @param string    $sortfield  Sort field
192
     * @param string    $sortorder  Sort order
193
     * @param int       $limit      Number of items per page
194
     * @param int       $page       Page number {@min 0}
195
     * @param int       $active     Payment type is active or not {@min 0} {@max 1}
196
     * @param string    $sqlfilters SQL criteria to filter with. Syntax example "(t.code:=:'CHQ')"
197
     *
198
     * @url     GET dictionary/payment_types
199
     *
200
     * @return array [List of payment types]
201
     *
202
     * @throws  RestException   400     Bad value for sqlfilters
203
     * @throws  RestException   403     Access denied
204
     * @throws  RestException   503     Error retrieving list of payment types
205
     */
206
    public function getPaymentTypes($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '')
207
    {
208
        $list = array();
209
210
        if (!DolibarrApiAccess::$user->hasRight('propal', 'lire') && !DolibarrApiAccess::$user->hasRight('commande', 'lire') && !DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
211
            throw new RestException(403);
212
        }
213
214
        $sql = "SELECT id, code, type, libelle as label, module";
215
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_paiement as t";
216
        $sql .= " WHERE t.entity IN (" . getEntity('c_paiement') . ")";
217
        $sql .= " AND t.active = " . ((int) $active);
218
        // Add sql filters
219
        if ($sqlfilters) {
220
            $errormessage = '';
221
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
222
            if ($errormessage) {
223
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
224
            }
225
        }
226
227
228
        $sql .= $this->db->order($sortfield, $sortorder);
229
230
        if ($limit) {
231
            if ($page < 0) {
232
                $page = 0;
233
            }
234
            $offset = $limit * $page;
235
236
            $sql .= $this->db->plimit($limit, $offset);
237
        }
238
239
        $result = $this->db->query($sql);
240
241
        if ($result) {
242
            $num = $this->db->num_rows($result);
243
            $min = min($num, ($limit <= 0 ? $num : $limit));
244
            for ($i = 0; $i < $min; $i++) {
245
                $list[] = $this->db->fetch_object($result);
246
            }
247
        } else {
248
            throw new RestException(503, $this->db->lasterror());
249
        }
250
251
        return $list;
252
    }
253
    /**
254
     * Get the list of regions.
255
     *
256
     * The returned list is sorted by region ID.
257
     *
258
     * @param string    $sortfield  Sort field
259
     * @param string    $sortorder  Sort order
260
     * @param int       $limit      Number of items per page
261
     * @param int       $page       Page number (starting from zero)
262
     * @param int       $country    To filter on country
263
     * @param string    $filter     To filter the regions by name
264
     * @param string    $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)"
265
     * @return array                List of regions
266
     *
267
     * @url     GET dictionary/regions
268
     *
269
     * @throws  RestException   400     Bad value for sqlfilters
270
     * @throws  RestException   503     Error retrieving list of regions
271
     */
272
    public function getListOfRegions($sortfield = "code_region", $sortorder = 'ASC', $limit = 100, $page = 0, $country = 0, $filter = '', $sqlfilters = '')
273
    {
274
        $list = array();
275
276
        // Note: The filter is not applied in the SQL request because it must
277
        // be applied to the translated names, not to the names in database.
278
        $sql = "SELECT t.rowid FROM " . MAIN_DB_PREFIX . "c_regions as t";
279
        $sql .= " WHERE 1 = 1";
280
        if ($country) {
281
            $sql .= " AND t.fk_pays = " . ((int) $country);
282
        }
283
        // Add sql filters
284
        if ($sqlfilters) {
285
            $errormessage = '';
286
            if (!DolibarrApi::_checkFilters($sqlfilters, $errormessage)) {
287
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
288
            }
289
            $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)';
290
            $sql .= " AND (" . preg_replace_callback('/' . $regexstring . '/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters) . ")";
291
        }
292
293
        $sql .= $this->db->order($sortfield, $sortorder);
294
295
        if ($limit) {
296
            if ($page < 0) {
297
                $page = 0;
298
            }
299
            $offset = $limit * $page;
300
301
            $sql .= $this->db->plimit($limit, $offset);
302
        }
303
304
        $result = $this->db->query($sql);
305
306
        if ($result) {
307
            $num = $this->db->num_rows($result);
308
            $min = min($num, ($limit <= 0 ? $num : $limit));
309
            for ($i = 0; $i < $min; $i++) {
310
                $obj = $this->db->fetch_object($result);
311
                $region = new Cregion($this->db);
312
                if ($region->fetch($obj->rowid) > 0) {
313
                    if (empty($filter) || stripos($region->name, $filter) !== false) {
314
                        $list[] = $this->_cleanObjectDatas($region);
315
                    }
316
                }
317
            }
318
        } else {
319
            throw new RestException(503, 'Error when retrieving list of regions');
320
        }
321
322
        return $list;
323
    }
324
325
    /**
326
     * Get region by ID.
327
     *
328
     * @param   int       $id       ID of region
329
     * @return  array              Object with cleaned properties
330
     *
331
     * @url     GET dictionary/regions/{id}
332
     *
333
     * @throws  RestException   404     Region not found
334
     * @throws  RestException   503     Error retrieving region
335
     */
336
    public function getRegionByID($id)
337
    {
338
        return $this->_fetchCregion($id, '');
339
    }
340
341
    /**
342
     * Get region by Code.
343
     *
344
     * @param   string    $code     Code of region
345
     * @return  array              Object with cleaned properties
346
     *
347
     * @url     GET dictionary/regions/byCode/{code}
348
     *
349
     * @throws  RestException   404     Region not found
350
     * @throws  RestException   503     Error when retrieving region
351
     */
352
    public function getRegionByCode($code)
353
    {
354
        return $this->_fetchCregion('', $code);
355
    }
356
357
    /**
358
     * Get the list of states/provinces.
359
     *
360
     * The names of the states will be translated to the given language if
361
     * the $lang parameter is provided. The value of $lang must be a language
362
     * code supported by Dolibarr, for example 'en_US' or 'fr_FR'.
363
     * The returned list is sorted by state ID.
364
     *
365
     * @param string    $sortfield  Sort field
366
     * @param string    $sortorder  Sort order
367
     * @param int       $limit      Number of items per page
368
     * @param int       $page       Page number (starting from zero)
369
     * @param int       $country    To filter on country
370
     * @param string    $filter     To filter the states by name
371
     * @param string    $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)"
372
     * @return array                List of states
373
     *
374
     * @url     GET dictionary/states
375
     *
376
     * @throws  RestException   400     Bad value for sqlfilters
377
     * @throws  RestException   503     Error retrieving list of states
378
     */
379
    public function getListOfStates($sortfield = "code_departement", $sortorder = 'ASC', $limit = 100, $page = 0, $country = 0, $filter = '', $sqlfilters = '')
380
    {
381
        $list = array();
382
383
        // Note: The filter is not applied in the SQL request because it must
384
        // be applied to the translated names, not to the names in database.
385
        $sql = "SELECT t.rowid FROM " . MAIN_DB_PREFIX . "c_departements as t";
386
        if ($country) {
387
            $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_regions as d ON t.fk_region = d.code_region";
388
        }
389
        $sql .= " WHERE 1 = 1";
390
        if ($country) {
391
            $sql .= " AND d.fk_pays = " . ((int) $country);
392
        }
393
        // Add sql filters
394
        if ($sqlfilters) {
395
            $errormessage = '';
396
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
397
            if ($errormessage) {
398
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
399
            }
400
        }
401
402
        $sql .= $this->db->order($sortfield, $sortorder);
403
404
        if ($limit) {
405
            if ($page < 0) {
406
                $page = 0;
407
            }
408
            $offset = $limit * $page;
409
410
            $sql .= $this->db->plimit($limit, $offset);
411
        }
412
413
        $result = $this->db->query($sql);
414
415
        if ($result) {
416
            $num = $this->db->num_rows($result);
417
            $min = min($num, ($limit <= 0 ? $num : $limit));
418
            for ($i = 0; $i < $min; $i++) {
419
                $obj = $this->db->fetch_object($result);
420
                $state = new Cstate($this->db);
421
                if ($state->fetch($obj->rowid) > 0) {
422
                    if (empty($filter) || stripos($state->label, $filter) !== false) {
423
                        $list[] = $this->_cleanObjectDatas($state);
424
                    }
425
                }
426
            }
427
        } else {
428
            throw new RestException(503, 'Error when retrieving list of states');
429
        }
430
431
        return $list;
432
    }
433
434
    /**
435
     * Get state by ID.
436
     *
437
     * @param   int       $id           ID of state
438
     * @return  array                  Object with cleaned properties
439
     *
440
     * @url     GET dictionary/states/{id}
441
     *
442
     * @throws  RestException   404     State not found
443
     * @throws  RestException   503     Error retrieving state
444
     */
445
    public function getStateByID($id)
446
    {
447
        return $this->_fetchCstate($id, '');
448
    }
449
450
    /**
451
     * Get state by Code.
452
     *
453
     * @param   string    $code         Code of state
454
     * @return  array                  Object with cleaned properties
455
     *
456
     * @url     GET dictionary/states/byCode/{code}
457
     *
458
     * @throws  RestException   404     State not found
459
     * @throws  RestException   503     Error retrieving state
460
     */
461
    public function getStateByCode($code)
462
    {
463
        return $this->_fetchCstate('', $code);
464
    }
465
466
    /**
467
     * Get the list of countries.
468
     *
469
     * The names of the countries will be translated to the given language if
470
     * the $lang parameter is provided. The value of $lang must be a language
471
     * code supported by Dolibarr, for example 'en_US' or 'fr_FR'.
472
     * The returned list is sorted by country ID.
473
     *
474
     * @param string    $sortfield  Sort field
475
     * @param string    $sortorder  Sort order
476
     * @param int       $limit      Number of items per page
477
     * @param int       $page       Page number (starting from zero)
478
     * @param string    $filter     To filter the countries by name
479
     * @param string    $lang       Code of the language the label of the countries must be translated to
480
     * @param string    $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)"
481
     * @return array                List of countries
482
     *
483
     * @url     GET dictionary/countries
484
     *
485
     * @throws  RestException   400     Bad value for sqlfilters
486
     * @throws  RestException   503     Error retrieving list of countries
487
     */
488
    public function getListOfCountries($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $filter = '', $lang = '', $sqlfilters = '')
489
    {
490
        $list = array();
491
492
        // Note: The filter is not applied in the SQL request because it must
493
        // be applied to the translated names, not to the names in database.
494
        $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "c_country as t";
495
        $sql .= " WHERE 1 = 1";
496
        // Add sql filters
497
        if ($sqlfilters) {
498
            $errormessage = '';
499
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
500
            if ($errormessage) {
501
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
502
            }
503
        }
504
505
        $sql .= $this->db->order($sortfield, $sortorder);
506
507
        if ($limit) {
508
            if ($page < 0) {
509
                $page = 0;
510
            }
511
            $offset = $limit * $page;
512
513
            $sql .= $this->db->plimit($limit, $offset);
514
        }
515
516
        $result = $this->db->query($sql);
517
518
        if ($result) {
519
            $num = $this->db->num_rows($result);
520
            $min = min($num, ($limit <= 0 ? $num : $limit));
521
            for ($i = 0; $i < $min; $i++) {
522
                $obj = $this->db->fetch_object($result);
523
                $country = new Ccountry($this->db);
524
                if ($country->fetch($obj->rowid) > 0) {
525
                    // Translate the name of the country if needed
526
                    // and then apply the filter if there is one.
527
                    $this->translateLabel($country, $lang, 'Country');
528
529
                    if (empty($filter) || stripos($country->label, $filter) !== false) {
530
                        $list[] = $this->_cleanObjectDatas($country);
531
                    }
532
                }
533
            }
534
        } else {
535
            throw new RestException(503, 'Error when retrieving list of countries');
536
        }
537
538
        return $list;
539
    }
540
541
    /**
542
     * Get country by ID.
543
     *
544
     * @param   int       $id           ID of country
545
     * @param   string    $lang         Code of the language the name of the country must be translated to
546
     * @return  array                  Object with cleaned properties
547
     *
548
     * @url     GET dictionary/countries/{id}
549
     *
550
     * @throws  RestException   404     Country not found
551
     * @throws  RestException   503     Error retrieving country
552
     */
553
    public function getCountryByID($id, $lang = '')
554
    {
555
        return $this->_fetchCcountry($id, '', '', $lang);
556
    }
557
558
    /**
559
     * Get country by Code.
560
     *
561
     * @param   string    $code         Code of country (2 characters)
562
     * @param   string    $lang         Code of the language the name of the country must be translated to
563
     * @return  array                  Object with cleaned properties
564
     *
565
     * @url     GET dictionary/countries/byCode/{code}
566
     *
567
     * @throws  RestException   404     Country not found
568
     * @throws  RestException   503     Error retrieving country
569
     */
570
    public function getCountryByCode($code, $lang = '')
571
    {
572
        return $this->_fetchCcountry('', $code, '', $lang);
573
    }
574
575
    /**
576
     * Get country by Iso.
577
     *
578
     * @param   string    $iso          ISO of country (3 characters)
579
     * @param   string    $lang         Code of the language the name of the country must be translated to
580
     * @return  array                  Object with cleaned properties
581
     *
582
     * @url     GET dictionary/countries/byISO/{iso}
583
     *
584
     * @throws  RestException   404     Country not found
585
     * @throws  RestException   503     Error retrieving country
586
     */
587
    public function getCountryByISO($iso, $lang = '')
588
    {
589
        return $this->_fetchCcountry('', '', $iso, $lang);
590
    }
591
592
    /**
593
     * Get region.
594
     *
595
     * @param   int       $id       ID of region
596
     * @param   string    $code     Code of region
597
     * @return  Object              Object with cleaned properties
598
     *
599
     * @throws RestException
600
     */
601
    private function _fetchCregion($id, $code = '')
602
    {
603
        $region = new Cregion($this->db);
604
605
        $result = $region->fetch($id, $code);
606
        if ($result < 0) {
607
            throw new RestException(503, 'Error when retrieving region : ' . $region->error);
608
        } elseif ($result == 0) {
609
            throw new RestException(404, 'Region not found');
610
        }
611
612
        return $this->_cleanObjectDatas($region);
613
    }
614
615
    /**
616
     * Get state.
617
     *
618
     * @param   int       $id           ID of state
619
     * @param   string    $code         Code of state
620
     * @return  Object                  Object with cleaned properties
621
     *
622
     * @throws RestException
623
     */
624
    private function _fetchCstate($id, $code = '')
625
    {
626
        $state = new Cstate($this->db);
627
628
        $result = $state->fetch($id, $code);
629
        if ($result < 0) {
630
            throw new RestException(503, 'Error when retrieving state : ' . $state->error);
631
        } elseif ($result == 0) {
632
            throw new RestException(404, 'State not found');
633
        }
634
635
        return $this->_cleanObjectDatas($state);
636
    }
637
638
    /**
639
     * Get country.
640
     *
641
     * @param   int       $id           ID of country
642
     * @param   string    $code         Code of country (2 characters)
643
     * @param   string    $iso          ISO of country (3 characters)
644
     * @param   string    $lang         Code of the language the name of the country must be translated to
645
     * @return  Object                  Object with cleaned properties
646
     *
647
     * @throws RestException
648
     */
649
    private function _fetchCcountry($id, $code = '', $iso = '', $lang = '')
650
    {
651
        $country = new Ccountry($this->db);
652
653
        $result = $country->fetch($id, $code, $iso);
654
655
        if ($result < 0) {
656
            throw new RestException(503, 'Error when retrieving country : ' . $country->error);
657
        } elseif ($result == 0) {
658
            throw new RestException(404, 'Country not found');
659
        }
660
661
        $this->translateLabel($country, $lang, 'Country');
662
663
        return $this->_cleanObjectDatas($country);
664
    }
665
666
    /**
667
     * Get the list of delivery times.
668
     *
669
     * @param string    $sortfield      Sort field
670
     * @param string    $sortorder      Sort order
671
     * @param int       $limit          Number of items per page
672
     * @param int       $page           Page number {@min 0}
673
     * @param int       $active         Delivery times is active or not {@min 0} {@max 1}
674
     * @param string    $sqlfilters     SQL criteria to filter with.
675
     *
676
     * @url     GET dictionary/availability
677
     *
678
     * @return array [List of availability]
679
     *
680
     * @throws  RestException   400     Bad value for sqlfilters
681
     * @throws  RestException   403     Access denied
682
     * @throws  RestException   503     Error when retrieving list of availabilities
683
     */
684
    public function getAvailability($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '')
685
    {
686
        $list = array();
687
688
        if (!DolibarrApiAccess::$user->hasRight('commande', 'lire')) {
689
            throw new RestException(403);
690
        }
691
692
        $sql = "SELECT rowid, code, label";
693
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_availability as t";
694
        $sql .= " WHERE t.active = " . ((int) $active);
695
        // Add sql filters
696
        if ($sqlfilters) {
697
            $errormessage = '';
698
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
699
            if ($errormessage) {
700
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
701
            }
702
        }
703
704
705
        $sql .= $this->db->order($sortfield, $sortorder);
706
707
        if ($limit) {
708
            if ($page < 0) {
709
                $page = 0;
710
            }
711
            $offset = $limit * $page;
712
713
            $sql .= $this->db->plimit($limit, $offset);
714
        }
715
716
        $result = $this->db->query($sql);
717
718
        if ($result) {
719
            $num = $this->db->num_rows($result);
720
            $min = min($num, ($limit <= 0 ? $num : $limit));
721
            for ($i = 0; $i < $min; $i++) {
722
                $list[] = $this->db->fetch_object($result);
723
            }
724
        } else {
725
            throw new RestException(503, $this->db->lasterror());
726
        }
727
728
        return $list;
729
    }
730
731
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
732
    /**
733
     * Clean sensible object datas
734
     *
735
     * @param   Object    $object       Object to clean
736
     * @return  Object                  Object with cleaned properties
737
     */
738
    protected function _cleanObjectDatas($object)
739
    {
740
		// phpcs:enable
741
        $object = parent::_cleanObjectDatas($object);
742
743
        unset($object->error);
744
        unset($object->errors);
745
746
        return $object;
747
    }
748
749
    /**
750
     * Translate the name of the object to the given language.
751
     *
752
     * @param object   $object    Object with label to translate
753
     * @param string   $lang      Code of the language the name of the object must be translated to
754
     * @param string   $prefix    Prefix for translation key
755
     * @param array    $dict      Array of dictionary for translation
756
     * @return void
757
     */
758
    private function translateLabel($object, $lang, $prefix = 'Country', $dict = array('dict'))
759
    {
760
        if (!empty($lang)) {
761
            // Load the translations if this is a new language.
762
            if ($this->translations == null || $this->translations->getDefaultLang() !== $lang) {
763
                global $conf;
764
                $this->translations = new Translate('', $conf);
765
                $this->translations->setDefaultLang($lang);
766
                $this->translations->loadLangs($dict);
767
            }
768
            if ($object->code) {
769
                $key = $prefix . $object->code;
770
771
                $translation = $this->translations->trans($key);
772
                if ($translation != $key) {
773
                    $object->label = html_entity_decode($translation);
774
                }
775
            }
776
        }
777
    }
778
779
    /**
780
     * Get the list of events types.
781
     *
782
     * @param string    $sortfield  Sort field
783
     * @param string    $sortorder  Sort order
784
     * @param int       $limit      Number of items per page
785
     * @param int       $page       Page number (starting from zero)
786
     * @param string    $type       To filter on type of event
787
     * @param string    $module     To filter on module events
788
     * @param int       $active     Event's type is active or not {@min 0} {@max 1}
789
     * @param string    $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)"
790
     * @return array                List of events types
791
     *
792
     * @url     GET dictionary/event_types
793
     *
794
     * @throws  RestException   400     Bad value for sqlfilters
795
     * @throws  RestException   503     Error when retrieving list of events types
796
     */
797
    public function getListOfEventTypes($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $type = '', $module = '', $active = 1, $sqlfilters = '')
798
    {
799
        $list = array();
800
801
        $sql = "SELECT id, code, type, libelle as label, module";
802
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_actioncomm as t";
803
        $sql .= " WHERE t.active = " . ((int) $active);
804
        if ($type) {
805
            $sql .= " AND t.type LIKE '%" . $this->db->escape($type) . "%'";
806
        }
807
        if ($module) {
808
            $sql .= " AND t.module LIKE '%" . $this->db->escape($module) . "%'";
809
        }
810
        // Add sql filters
811
        if ($sqlfilters) {
812
            $errormessage = '';
813
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
814
            if ($errormessage) {
815
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
816
            }
817
        }
818
819
820
        $sql .= $this->db->order($sortfield, $sortorder);
821
822
        if ($limit) {
823
            if ($page < 0) {
824
                $page = 0;
825
            }
826
            $offset = $limit * $page;
827
828
            $sql .= $this->db->plimit($limit, $offset);
829
        }
830
831
        $result = $this->db->query($sql);
832
833
        if ($result) {
834
            $num = $this->db->num_rows($result);
835
            $min = min($num, ($limit <= 0 ? $num : $limit));
836
            for ($i = 0; $i < $min; $i++) {
837
                $list[] = $this->db->fetch_object($result);
838
            }
839
        } else {
840
            throw new RestException(503, 'Error when retrieving list of events types : ' . $this->db->lasterror());
841
        }
842
843
        return $list;
844
    }
845
846
847
    /**
848
     * Get the list of Expense Report types.
849
     *
850
     * @param string    $sortfield  Sort field
851
     * @param string    $sortorder  Sort order
852
     * @param int       $limit      Number of items per page
853
     * @param int       $page       Page number (starting from zero)
854
     * @param string    $module     To filter on module
855
     * @param int       $active     Event's type is active or not {@min 0} {@max 1}
856
     * @param string    $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)"
857
     * @return array                List of expense report types
858
     *
859
     * @url     GET dictionary/expensereport_types
860
     *
861
     * @throws  RestException   400     Bad value for sqlfilters
862
     * @throws  RestException   503     Error when retrieving list of expense report types
863
     */
864
    public function getListOfExpenseReportsTypes($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $module = '', $active = 1, $sqlfilters = '')
865
    {
866
        $list = array();
867
868
        $sql = "SELECT id, code, label, accountancy_code, active, module, position";
869
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_type_fees as t";
870
        $sql .= " WHERE t.active = " . ((int) $active);
871
        if ($module) {
872
            $sql .= " AND t.module LIKE '%" . $this->db->escape($module) . "%'";
873
        }
874
        // Add sql filters
875
        if ($sqlfilters) {
876
            $errormessage = '';
877
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
878
            if ($errormessage) {
879
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
880
            }
881
        }
882
883
884
        $sql .= $this->db->order($sortfield, $sortorder);
885
886
        if ($limit) {
887
            if ($page < 0) {
888
                $page = 0;
889
            }
890
            $offset = $limit * $page;
891
892
            $sql .= $this->db->plimit($limit, $offset);
893
        }
894
895
        $result = $this->db->query($sql);
896
897
        if ($result) {
898
            $num = $this->db->num_rows($result);
899
            $min = min($num, ($limit <= 0 ? $num : $limit));
900
            for ($i = 0; $i < $min; $i++) {
901
                $list[] = $this->db->fetch_object($result);
902
            }
903
        } else {
904
            throw new RestException(503, 'Error when retrieving list of expense report types : ' . $this->db->lasterror());
905
        }
906
907
        return $list;
908
    }
909
910
911
    /**
912
     * Get the list of contacts types.
913
     *
914
     * @param string    $sortfield  Sort field
915
     * @param string    $sortorder  Sort order
916
     * @param int       $limit      Number of items per page
917
     * @param int       $page       Page number (starting from zero)
918
     * @param string    $type       To filter on type of contact
919
     * @param string    $module     To filter on module contacts
920
     * @param int       $active     Contact's type is active or not {@min 0} {@max 1}
921
     * @param string    $lang       Code of the language the label of the civility must be translated to
922
     * @param string    $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)"
923
     * @return array      List of Contacts types
924
     *
925
     * @url     GET dictionary/contact_types
926
     *
927
     * @throws  RestException   400     Bad value for sqlfilters
928
     * @throws  RestException   503     Error when retrieving list of contacts types
929
     */
930
    public function getListOfContactTypes($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $type = '', $module = '', $active = 1, $lang = '', $sqlfilters = '')
931
    {
932
        $list = array();
933
934
        $sql = "SELECT rowid, code, element as type, libelle as label, source, module, position";
935
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_type_contact as t";
936
        $sql .= " WHERE t.active = " . ((int) $active);
937
        if ($type) {
938
            $sql .= " AND type LIKE '%" . $this->db->escape($type) . "%'";
939
        }
940
        if ($module) {
941
            $sql .= " AND t.module LIKE '%" . $this->db->escape($module) . "%'";
942
        }
943
        // Add sql filters
944
        if ($sqlfilters) {
945
            $errormessage = '';
946
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
947
            if ($errormessage) {
948
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
949
            }
950
        }
951
952
953
        $sql .= $this->db->order($sortfield, $sortorder);
954
955
        if ($limit) {
956
            if ($page < 0) {
957
                $page = 0;
958
            }
959
            $offset = $limit * $page;
960
961
            $sql .= $this->db->plimit($limit, $offset);
962
        }
963
964
        $result = $this->db->query($sql);
965
966
        if ($result) {
967
            $num = $this->db->num_rows($result);
968
            $min = min($num, ($limit <= 0 ? $num : $limit));
969
            for ($i = 0; $i < $min; $i++) {
970
                $contact_type = $this->db->fetch_object($result);
971
                $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"));
972
                $list[] = $contact_type;
973
            }
974
        } else {
975
            throw new RestException(503, 'Error when retrieving list of contacts types : ' . $this->db->lasterror());
976
        }
977
978
        return $list;
979
    }
980
981
    /**
982
     * Get the list of civilities.
983
     *
984
     * @param string    $sortfield  Sort field
985
     * @param string    $sortorder  Sort order
986
     * @param int       $limit      Number of items per page
987
     * @param int       $page       Page number (starting from zero)
988
     * @param string    $module     To filter on module events
989
     * @param int       $active     Civility is active or not {@min 0} {@max 1}
990
     * @param string    $lang       Code of the language the label of the civility must be translated to
991
     * @param string    $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)"
992
     * @return array        List of civility types
993
     *
994
     * @url     GET dictionary/civilities
995
     *
996
     * @throws  RestException   400     Bad value for sqlfilters
997
     * @throws  RestException   503     Error when retrieving list of civilities
998
     */
999
    public function getListOfCivilities($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $module = '', $active = 1, $lang = '', $sqlfilters = '')
1000
    {
1001
        $list = array();
1002
1003
        $sql = "SELECT rowid, code, label, module";
1004
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_civility as t";
1005
        $sql .= " WHERE t.active = " . ((int) $active);
1006
        if ($module) {
1007
            $sql .= " AND t.module LIKE '%" . $this->db->escape($module) . "%'";
1008
        }
1009
        // Add sql filters
1010
        if ($sqlfilters) {
1011
            $errormessage = '';
1012
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
1013
            if ($errormessage) {
1014
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
1015
            }
1016
        }
1017
1018
1019
        $sql .= $this->db->order($sortfield, $sortorder);
1020
1021
        if ($limit) {
1022
            if ($page < 0) {
1023
                $page = 0;
1024
            }
1025
            $offset = $limit * $page;
1026
1027
            $sql .= $this->db->plimit($limit, $offset);
1028
        }
1029
1030
        $result = $this->db->query($sql);
1031
1032
        if ($result) {
1033
            $num = $this->db->num_rows($result);
1034
            $min = min($num, ($limit <= 0 ? $num : $limit));
1035
            for ($i = 0; $i < $min; $i++) {
1036
                $civility = $this->db->fetch_object($result);
1037
                $this->translateLabel($civility, $lang, 'Civility', array('dict'));
1038
                $list[] = $civility;
1039
            }
1040
        } else {
1041
            throw new RestException(503, 'Error when retrieving list of civility : ' . $this->db->lasterror());
1042
        }
1043
1044
        return $list;
1045
    }
1046
1047
    /**
1048
     * Get the list of currencies.
1049
     *
1050
     * @param int       $multicurrency  Multicurrency rates (0: no multicurrency, 1: last rate, 2: all rates) {@min 0} {@max 2}
1051
     * @param string    $sortfield  Sort field
1052
     * @param string    $sortorder  Sort order
1053
     * @param int       $limit      Number of items per page
1054
     * @param int       $page       Page number (starting from zero)
1055
     * @param int       $active     Payment term is active or not {@min 0} {@max 1}
1056
     * @param string    $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)"
1057
     * @return array                List of currencies
1058
     *
1059
     * @url     GET dictionary/currencies
1060
     *
1061
     * @throws  RestException   400     Bad value for sqlfilters
1062
     * @throws  RestException   503     Error when retrieving list of currencies
1063
     */
1064
    public function getListOfCurrencies($multicurrency = 0, $sortfield = "code_iso", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '')
1065
    {
1066
        $list = array();
1067
        $sql = "SELECT t.code_iso, t.label, t.unicode";
1068
        if (!empty($multicurrency)) {
1069
            $sql .= " , cr.date_sync, cr.rate ";
1070
        }
1071
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_currencies as t";
1072
        if (!empty($multicurrency)) {
1073
            $sql .= " JOIN " . MAIN_DB_PREFIX . "multicurrency as m ON m.code=t.code_iso";
1074
            $sql .= " JOIN " . MAIN_DB_PREFIX . "multicurrency_rate as cr ON (m.rowid = cr.fk_multicurrency)";
1075
        }
1076
        $sql .= " WHERE t.active = " . ((int) $active);
1077
        if (!empty($multicurrency)) {
1078
            $sql .= " AND m.entity IN (" . getEntity('multicurrency') . ")";
1079
            if (!empty($multicurrency) && $multicurrency != 2) {
1080
                $sql .= " AND cr.date_sync = (SELECT MAX(cr2.date_sync) FROM " . MAIN_DB_PREFIX . "multicurrency_rate AS cr2 WHERE cr2.fk_multicurrency = m.rowid)";
1081
            }
1082
        }
1083
1084
        // Add sql filters
1085
        if ($sqlfilters) {
1086
            $errormessage = '';
1087
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
1088
            if ($errormessage) {
1089
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
1090
            }
1091
        }
1092
1093
1094
        $sql .= $this->db->order($sortfield, $sortorder);
1095
1096
        if ($limit) {
1097
            if ($page < 0) {
1098
                $page = 0;
1099
            }
1100
            $offset = $limit * $page;
1101
1102
            $sql .= $this->db->plimit($limit, $offset);
1103
        }
1104
1105
        $result = $this->db->query($sql);
1106
1107
        if ($result) {
1108
            $num = $this->db->num_rows($result);
1109
            $min = min($num, ($limit <= 0 ? $num : $limit));
1110
            for ($i = 0; $i < $min; $i++) {
1111
                $list[] = $this->db->fetch_object($result);
1112
            }
1113
        } else {
1114
            throw new RestException(503, 'Error when retrieving list of currency : ' . $this->db->lasterror());
1115
        }
1116
1117
        return $list;
1118
    }
1119
1120
    /**
1121
     * Get the list of extra fields.
1122
     *
1123
     * @param string    $sortfield  Sort field
1124
     * @param string    $sortorder  Sort order
1125
     * @param string    $elementtype       Type of element ('adherent', 'commande', 'thirdparty', 'facture', 'propal', 'product', ...)
1126
     * @param string    $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.label:like:'SO-%')"
1127
     * @return array                List of extra fields
1128
     *
1129
     * @url     GET extrafields
1130
     *
1131
     * @throws  RestException   400     Bad value for sqlfilters
1132
     * @throws  RestException   503     Error when retrieving list of extra fields
1133
     */
1134
    public function getListOfExtrafields($sortfield = "t.pos", $sortorder = 'ASC', $elementtype = '', $sqlfilters = '')
1135
    {
1136
        $list = array();
1137
1138
        if (!DolibarrApiAccess::$user->admin) {
1139
            throw new RestException(403, 'Only an admin user can get list of extrafields');
1140
        }
1141
1142
        if ($elementtype == 'thirdparty') {
1143
            $elementtype = 'societe';
1144
        }
1145
        if ($elementtype == 'contact') {
1146
            $elementtype = 'socpeople';
1147
        }
1148
1149
        $sql = "SELECT t.rowid as id, t.name, t.entity, t.elementtype, t.label, t.type, t.size, t.fieldcomputed, t.fielddefault,";
1150
        $sql .= " t.fieldunique, t.fieldrequired, t.perms, t.enabled, t.pos, t.alwayseditable, t.param, t.list, t.printable,";
1151
        $sql .= " t.totalizable, t.langs, t.help, t.css, t.cssview, t.fk_user_author, t.fk_user_modif, t.datec, t.tms";
1152
        $sql .= " FROM " . MAIN_DB_PREFIX . "extrafields as t";
1153
        $sql .= " WHERE t.entity IN (" . getEntity('extrafields') . ")";
1154
        if (!empty($elementtype)) {
1155
            $sql .= " AND t.elementtype = '" . $this->db->escape($elementtype) . "'";
1156
        }
1157
        // Add sql filters
1158
        if ($sqlfilters) {
1159
            $errormessage = '';
1160
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
1161
            if ($errormessage) {
1162
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
1163
            }
1164
        }
1165
1166
        $sql .= $this->db->order($sortfield, $sortorder);
1167
1168
        $resql = $this->db->query($sql);
1169
        if ($resql) {
1170
            if ($this->db->num_rows($resql)) {
1171
                while ($tab = $this->db->fetch_object($resql)) {
1172
                    // New usage
1173
                    $list[$tab->elementtype][$tab->name]['id'] = $tab->id;
1174
                    $list[$tab->elementtype][$tab->name]['type'] = $tab->type;
1175
                    $list[$tab->elementtype][$tab->name]['label'] = $tab->label;
1176
                    $list[$tab->elementtype][$tab->name]['size'] = $tab->size;
1177
                    $list[$tab->elementtype][$tab->name]['elementtype'] = $tab->elementtype;
1178
                    $list[$tab->elementtype][$tab->name]['default'] = $tab->fielddefault;
1179
                    $list[$tab->elementtype][$tab->name]['computed'] = $tab->fieldcomputed;
1180
                    $list[$tab->elementtype][$tab->name]['unique'] = $tab->fieldunique;
1181
                    $list[$tab->elementtype][$tab->name]['required'] = $tab->fieldrequired;
1182
                    $list[$tab->elementtype][$tab->name]['param'] = ($tab->param ? jsonOrUnserialize($tab->param) : '');    // This may be a string encoded with serialise() or json_encode()
1183
                    $list[$tab->elementtype][$tab->name]['pos'] = $tab->pos;
1184
                    $list[$tab->elementtype][$tab->name]['alwayseditable'] = $tab->alwayseditable;
1185
                    $list[$tab->elementtype][$tab->name]['perms'] = $tab->perms;
1186
                    $list[$tab->elementtype][$tab->name]['list'] = $tab->list;
1187
                    $list[$tab->elementtype][$tab->name]['printable'] = $tab->printable;
1188
                    $list[$tab->elementtype][$tab->name]['totalizable'] = $tab->totalizable;
1189
                    $list[$tab->elementtype][$tab->name]['langs'] = $tab->langs;
1190
                    $list[$tab->elementtype][$tab->name]['help'] = $tab->help;
1191
                    $list[$tab->elementtype][$tab->name]['css'] = $tab->css;
1192
                    $list[$tab->elementtype][$tab->name]['cssview'] = $tab->cssview;
1193
                    $list[$tab->elementtype][$tab->name]['csslist'] = $tab->csslist;
1194
                    $list[$tab->elementtype][$tab->name]['fk_user_author'] = $tab->fk_user_author;
1195
                    $list[$tab->elementtype][$tab->name]['fk_user_modif'] = $tab->fk_user_modif;
1196
                    $list[$tab->elementtype][$tab->name]['datec'] = $tab->datec;
1197
                    $list[$tab->elementtype][$tab->name]['tms'] = $tab->tms;
1198
                }
1199
            }
1200
        } else {
1201
            throw new RestException(503, 'Error when retrieving list of extra fields : ' . $this->db->lasterror());
1202
        }
1203
1204
        return $list;
1205
    }
1206
1207
    /**
1208
     * Delete extrafield
1209
     *
1210
     * @param   string     $attrname         extrafield attrname
1211
     * @param   string     $elementtype      extrafield elementtype
1212
     * @return  array
1213
     *
1214
     * @url     DELETE extrafields/{elementtype}/{attrname}
1215
     *
1216
     */
1217
    public function deleteExtrafieldsFromNames($attrname, $elementtype)
1218
    {
1219
        if (!DolibarrApiAccess::$user->admin) {
1220
            throw new RestException(403, 'Only an admin user can delete an extrafield by attrname and elementtype');
1221
        }
1222
1223
        $extrafields = new ExtraFields($this->db);
1224
1225
        $result = $extrafields->fetch_name_optionals_label($elementtype, false, $attrname);
1226
        if (!$result) {
1227
            throw new RestException(404, 'Extrafield not found from attrname and elementtype');
1228
        }
1229
1230
        if (!$extrafields->delete($attrname, $elementtype)) {
1231
            throw new RestException(500, 'Error when delete extrafield : ' . $extrafields->error);
1232
        }
1233
1234
        return array(
1235
            'success' => array(
1236
                'code' => 200,
1237
                'message' => 'Extrafield deleted from attrname and elementtype'
1238
            )
1239
        );
1240
    }
1241
1242
1243
1244
    /** get Extrafield object
1245
     *
1246
     * @param   string  $attrname       extrafield attrname
1247
     * @param   string  $elementtype    extrafield elementtype
1248
     * @return  array                   List of extra fields
1249
     *
1250
     * @url     GET     extrafields/{elementtype}/{attrname}
1251
     *
1252
     * @suppress PhanPluginUnknownArrayMethodParamType  Luracast limitation
1253
     *
1254
     */
1255
    public function getExtrafields($attrname, $elementtype)
1256
    {
1257
        $answer = array();
1258
1259
        if (!DolibarrApiAccess::$user->admin) {
1260
            throw new RestException(403, 'Only an admin user can get list of extrafields');
1261
        }
1262
1263
        if ($elementtype == 'thirdparty') {
1264
            $elementtype = 'societe';
1265
        }
1266
        if ($elementtype == 'contact') {
1267
            $elementtype = 'socpeople';
1268
        }
1269
1270
        $sql = "SELECT t.rowid as id, t.name, t.entity, t.elementtype, t.label, t.type, t.size, t.fieldcomputed, t.fielddefault,";
1271
        $sql .= " t.fieldunique, t.fieldrequired, t.perms, t.enabled, t.pos, t.alwayseditable, t.param, t.list, t.printable,";
1272
        $sql .= " t.totalizable, t.langs, t.help, t.css, t.cssview, t.fk_user_author, t.fk_user_modif, t.datec, t.tms";
1273
        $sql .= " FROM " . MAIN_DB_PREFIX . "extrafields as t";
1274
        $sql .= " WHERE t.entity IN (" . getEntity('extrafields') . ")";
1275
        $sql .= " AND t.elementtype = '" . $this->db->escape($elementtype) . "'";
1276
        $sql .= " AND t.name = '" . $this->db->escape($attrname) . "'";
1277
1278
        $resql = $this->db->query($sql);
1279
        if ($resql) {
1280
            if ($this->db->num_rows($resql)) {
1281
                while ($tab = $this->db->fetch_object($resql)) {
1282
                    // New usage
1283
                    $answer[$tab->elementtype][$tab->name]['id'] = $tab->id;
1284
                    $answer[$tab->elementtype][$tab->name]['type'] = $tab->type;
1285
                    $answer[$tab->elementtype][$tab->name]['label'] = $tab->label;
1286
                    $answer[$tab->elementtype][$tab->name]['size'] = $tab->size;
1287
                    $answer[$tab->elementtype][$tab->name]['elementtype'] = $tab->elementtype;
1288
                    $answer[$tab->elementtype][$tab->name]['default'] = $tab->fielddefault;
1289
                    $answer[$tab->elementtype][$tab->name]['computed'] = $tab->fieldcomputed;
1290
                    $answer[$tab->elementtype][$tab->name]['unique'] = $tab->fieldunique;
1291
                    $answer[$tab->elementtype][$tab->name]['required'] = $tab->fieldrequired;
1292
                    $answer[$tab->elementtype][$tab->name]['param'] = ($tab->param ? jsonOrUnserialize($tab->param) : '');  // This may be a string encoded with serialise() or json_encode()
1293
                    $answer[$tab->elementtype][$tab->name]['pos'] = $tab->pos;
1294
                    $answer[$tab->elementtype][$tab->name]['alwayseditable'] = $tab->alwayseditable;
1295
                    $answer[$tab->elementtype][$tab->name]['perms'] = $tab->perms;
1296
                    $answer[$tab->elementtype][$tab->name]['list'] = $tab->list;
1297
                    $answer[$tab->elementtype][$tab->name]['printable'] = $tab->printable;
1298
                    $answer[$tab->elementtype][$tab->name]['totalizable'] = $tab->totalizable;
1299
                    $answer[$tab->elementtype][$tab->name]['langs'] = $tab->langs;
1300
                    $answer[$tab->elementtype][$tab->name]['help'] = $tab->help;
1301
                    $answer[$tab->elementtype][$tab->name]['css'] = $tab->css;
1302
                    $answer[$tab->elementtype][$tab->name]['cssview'] = $tab->cssview;
1303
                    $answer[$tab->elementtype][$tab->name]['csslist'] = $tab->csslist;
1304
                    $answer[$tab->elementtype][$tab->name]['fk_user_author'] = $tab->fk_user_author;
1305
                    $answer[$tab->elementtype][$tab->name]['fk_user_modif'] = $tab->fk_user_modif;
1306
                    $answer[$tab->elementtype][$tab->name]['datec'] = $tab->datec;
1307
                    $answer[$tab->elementtype][$tab->name]['tms'] = $tab->tms;
1308
                }
1309
            } else {
1310
                throw new RestException(404, 'Extrafield not found from attrname and elementtype');
1311
            }
1312
        } else {
1313
            throw new RestException(503, 'Error when retrieving list of extra fields : ' . $this->db->lasterror());
1314
        }
1315
1316
        return $answer;
1317
    }
1318
1319
    /**
1320
     * Create Extrafield object
1321
     *
1322
     * @param   string  $attrname       extrafield attrname
1323
     * @param   string  $elementtype    extrafield elementtype
1324
     * @param   array   $request_data   Request datas
1325
     * @return  int                     ID of extrafield
1326
     *
1327
     * @url     POST    extrafields/{elementtype}/{attrname}
1328
     *
1329
     * @suppress PhanPluginUnknownArrayMethodParamType  Luracast limitation
1330
     *
1331
     */
1332
    public function postExtrafields($attrname, $elementtype, $request_data = null)
1333
    {
1334
        if (!DolibarrApiAccess::$user->admin) {
1335
            throw new RestException(403, 'Only an admin user can create an extrafield');
1336
        }
1337
1338
        $extrafields = new ExtraFields($this->db);
1339
1340
        $result = $extrafields->fetch_name_optionals_label($elementtype, false, $attrname);
1341
        if ($result) {
1342
            throw new RestException(409, 'Duplicate extrafield already found from attrname and elementtype');
1343
        }
1344
1345
        // Check mandatory fields is not working despise being a modified copy from api_thirdparties.class.php
1346
        // $result = $this->_validateExtrafields($request_data, $extrafields);
1347
1348
        foreach ($request_data as $field => $value) {
1349
            $extrafields->$field = $this->_checkValForAPI($field, $value, $extrafields);
1350
        }
1351
1352
        $entity = DolibarrApiAccess::$user->entity;
1353
        if (empty($entity)) {
1354
            $entity = 1;
1355
        }
1356
1357
        // built in validation
1358
        $enabled = 1; // hardcoded because it seems to always be 1 in every row in the database
1359
1360
        if ($request_data['label']) {
1361
            $label = $request_data['label'];
1362
        } else {
1363
            throw new RestException(400, "label field absent in json at root level");
1364
        }
1365
1366
        $alwayseditable = $request_data['alwayseditable'];
1367
        $default_value = $request_data['default_value'];
1368
        $totalizable = $request_data['totalizable'];
1369
        $printable = $request_data['printable'];
1370
        $required = $request_data['required'];
1371
        $langfile = $request_data['langfile'];
1372
        $computed = $request_data['computed'];
1373
        $unique = $request_data['unique'];
1374
        $param = $request_data['param'];
1375
        $perms = $request_data['perms'];
1376
        $size = $request_data['size'];
1377
        $type = $request_data['type'];
1378
        $list = $request_data['list'];
1379
        $help = $request_data['help'];
1380
        $pos = $request_data['pos'];
1381
        $moreparams = array();
1382
1383
        if (0 > $extrafields->addExtraField($attrname, $label, $type, $pos, $size, $elementtype, $unique, $required, $default_value, $param, $alwayseditable, $perms, $list, $help, $computed, $entity, $langfile, $enabled, $totalizable, $printable, $moreparams)) {
1384
            throw new RestException(500, 'Error creating extrafield', array_merge(array($extrafields->errno), $extrafields->errors));
1385
        }
1386
1387
        $sql = "SELECT t.rowid as id";
1388
        $sql .= " FROM " . MAIN_DB_PREFIX . "extrafields as t";
1389
        $sql .= " WHERE elementtype = '" . $this->db->escape($elementtype) . "'";
1390
        $sql .= " AND name = '" . $this->db->escape($attrname) . "'";
1391
1392
        $resql = $this->db->query($sql);
1393
        if ($resql) {
1394
            if ($this->db->num_rows($resql)) {
1395
                $tab = $this->db->fetch_object($resql);
1396
                $id = (int) $tab->id;
1397
            } else {
1398
                $id = (int) -1;
1399
            }
1400
        } else {
1401
            $id = (int) -2;
1402
        }
1403
1404
        return $id;
1405
    }
1406
1407
    /**
1408
1409
     * Update Extrafield object
1410
     *
1411
     * @param   string  $attrname       extrafield attrname
1412
     * @param   string  $elementtype    extrafield elementtype
1413
     * @param   array   $request_data   Request datas
1414
     * @return  int                     ID of extrafield
1415
     *
1416
     * @url     PUT     extrafields/{elementtype}/{attrname}
1417
     *
1418
     * @suppress PhanPluginUnknownArrayMethodParamType  Luracast limitation
1419
     *
1420
     */
1421
    public function updateExtrafields($attrname, $elementtype, $request_data = null)
1422
    {
1423
        if (!DolibarrApiAccess::$user->admin) {
1424
            throw new RestException(403, 'Only an admin user can create an extrafield');
1425
        }
1426
1427
        $extrafields = new ExtraFields($this->db);
1428
1429
        $result = $extrafields->fetch_name_optionals_label($elementtype, false, $attrname);
1430
        if (!$result) {
1431
            throw new RestException(404, 'Extrafield not found from attrname and elementtype');
1432
        }
1433
1434
        foreach ($request_data as $field => $value) {
1435
            $extrafields->$field = $this->_checkValForAPI($field, $value, $extrafields);
1436
        }
1437
1438
        $entity = DolibarrApiAccess::$user->entity;
1439
        if (empty($entity)) {
1440
            $entity = 1;
1441
        }
1442
1443
        // built in validation
1444
        $enabled = 1; // hardcoded because it seems to always be 1 in every row in the database
1445
        if ($request_data['label']) {
1446
            $label = $request_data['label'];
1447
        } else {
1448
            throw new RestException(400, "label field absent in json at root level");
1449
        }
1450
1451
        $alwayseditable = $request_data['alwayseditable'];
1452
        $default_value = $request_data['default_value'];
1453
        $totalizable = $request_data['totalizable'];
1454
        $printable = $request_data['printable'];
1455
        $required = $request_data['required'];
1456
        $langfile = $request_data['langfile'];
1457
        $computed = $request_data['computed'];
1458
        $unique = $request_data['unique'];
1459
        $param = $request_data['param'];
1460
        $perms = $request_data['perms'];
1461
        $size = $request_data['size'];
1462
        $type = $request_data['type'];
1463
        $list = $request_data['list'];
1464
        $help = $request_data['help'];
1465
        $pos = $request_data['pos'];
1466
        $moreparams = array();
1467
1468
        dol_syslog(get_only_class($this) . '::updateExtraField', LOG_DEBUG);
1469
        if (0 > $extrafields->updateExtraField($attrname, $label, $type, $pos, $size, $elementtype, $unique, $required, $default_value, $param, $alwayseditable, $perms, $list, $help, $computed, $entity, $langfile, $enabled, $totalizable, $printable, $moreparams)) {
1470
            throw new RestException(500, 'Error updating extrafield', array_merge(array($extrafields->errno), $extrafields->errors));
1471
        }
1472
1473
        $sql = "SELECT t.rowid as id";
1474
        $sql .= " FROM " . MAIN_DB_PREFIX . "extrafields as t";
1475
        $sql .= " WHERE elementtype = '" . $this->db->escape($elementtype) . "'";
1476
        $sql .= " AND name = '" . $this->db->escape($attrname) . "'";
1477
1478
        $resql = $this->db->query($sql);
1479
        if ($resql) {
1480
            if ($this->db->num_rows($resql)) {
1481
                $tab = $this->db->fetch_object($resql);
1482
                $id = (int) $tab->id;
1483
            } else {
1484
                $id = (int) -1;
1485
            }
1486
        } else {
1487
            $id = (int) -2;
1488
        }
1489
1490
        return $id;
1491
    }
1492
1493
    /**
1494
     * Get the list of towns.
1495
     *
1496
     * @param string    $sortfield  Sort field
1497
     * @param string    $sortorder  Sort order
1498
     * @param int       $limit      Number of items per page
1499
     * @param int       $page       Page number (starting from zero)
1500
     * @param string    $zipcode    To filter on zipcode
1501
     * @param string    $town       To filter on city name
1502
     * @param int       $active     Town is active or not {@min 0} {@max 1}
1503
     * @param string    $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)"
1504
     * @return array                List of towns
1505
     *
1506
     * @url     GET dictionary/towns
1507
     *
1508
     * @throws  RestException   400     Bad value for sqlfilters
1509
     * @throws  RestException   503     Error when retrieving list of towns
1510
     */
1511
    public function getListOfTowns($sortfield = "zip,town", $sortorder = 'ASC', $limit = 100, $page = 0, $zipcode = '', $town = '', $active = 1, $sqlfilters = '')
1512
    {
1513
        $list = array();
1514
1515
        $sql = "SELECT rowid AS id, zip, town, fk_county, fk_pays AS fk_country";
1516
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_ziptown as t";
1517
        $sql .= " WHERE t.active = " . ((int) $active);
1518
        if ($zipcode) {
1519
            $sql .= " AND t.zip LIKE '%" . $this->db->escape($zipcode) . "%'";
1520
        }
1521
        if ($town) {
1522
            $sql .= " AND t.town LIKE '%" . $this->db->escape($town) . "%'";
1523
        }
1524
        // Add sql filters
1525
        if ($sqlfilters) {
1526
            $errormessage = '';
1527
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
1528
            if ($errormessage) {
1529
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
1530
            }
1531
        }
1532
1533
1534
        $sql .= $this->db->order($sortfield, $sortorder);
1535
1536
        if ($limit) {
1537
            if ($page < 0) {
1538
                $page = 0;
1539
            }
1540
            $offset = $limit * $page;
1541
1542
            $sql .= $this->db->plimit($limit, $offset);
1543
        }
1544
1545
        $result = $this->db->query($sql);
1546
1547
        if ($result) {
1548
            $num = $this->db->num_rows($result);
1549
            $min = min($num, ($limit <= 0 ? $num : $limit));
1550
            for ($i = 0; $i < $min; $i++) {
1551
                $list[] = $this->db->fetch_object($result);
1552
            }
1553
        } else {
1554
            throw new RestException(503, 'Error when retrieving list of towns : ' . $this->db->lasterror());
1555
        }
1556
1557
        return $list;
1558
    }
1559
1560
    /**
1561
     * Get the list of payments terms.
1562
     *
1563
     * @param string    $sortfield  Sort field
1564
     * @param string    $sortorder  Sort order
1565
     * @param int       $limit      Number of items per page
1566
     * @param int       $page       Page number {@min 0}
1567
     * @param int       $active     Payment term is active or not {@min 0} {@max 1}
1568
     * @param string    $sqlfilters SQL criteria to filter. Syntax example "(t.code:=:'CHQ')"
1569
     *
1570
     * @url     GET dictionary/payment_terms
1571
     *
1572
     * @return array List of payment terms
1573
     *
1574
     * @throws  RestException   400     Bad value for sqlfilters
1575
     * @throws  RestException   403     Access denied
1576
     * @throws  RestException   503     Error when retrieving list of payments terms
1577
     */
1578
    public function getPaymentTerms($sortfield = "sortorder", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '')
1579
    {
1580
        $list = array();
1581
1582
        if (!DolibarrApiAccess::$user->hasRight('propal', 'lire') && !DolibarrApiAccess::$user->hasRight('commande', 'lire') && !DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
1583
            throw new RestException(403);
1584
        }
1585
1586
        $sql = "SELECT rowid as id, code, sortorder, libelle as label, libelle_facture as descr, type_cdr, nbjour, decalage, module";
1587
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_payment_term as t";
1588
        $sql .= " WHERE t.entity IN (" . getEntity('c_payment_term') . ")";
1589
        $sql .= " AND t.active = " . ((int) $active);
1590
        // Add sql filters
1591
        if ($sqlfilters) {
1592
            $errormessage = '';
1593
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
1594
            if ($errormessage) {
1595
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
1596
            }
1597
        }
1598
1599
1600
        $sql .= $this->db->order($sortfield, $sortorder);
1601
1602
        if ($limit) {
1603
            if ($page < 0) {
1604
                $page = 0;
1605
            }
1606
            $offset = $limit * $page;
1607
1608
            $sql .= $this->db->plimit($limit, $offset);
1609
        }
1610
1611
        $result = $this->db->query($sql);
1612
1613
        if ($result) {
1614
            $num = $this->db->num_rows($result);
1615
            $min = min($num, ($limit <= 0 ? $num : $limit));
1616
            for ($i = 0; $i < $min; $i++) {
1617
                $list[] = $this->db->fetch_object($result);
1618
            }
1619
        } else {
1620
            throw new RestException(503, $this->db->lasterror());
1621
        }
1622
1623
        return $list;
1624
    }
1625
1626
    /**
1627
     * Get the list of shipping methods.
1628
     *
1629
     * @param int       $limit      Number of items per page
1630
     * @param int       $page       Page number {@min 0}
1631
     * @param int       $active     Shipping methodsm is active or not {@min 0} {@max 1}
1632
     * @param string    $lang       Code of the language the label of the method must be translated to
1633
     * @param string    $sqlfilters SQL criteria to filter. Syntax example "(t.code:=:'CHQ')"
1634
     *
1635
     * @url     GET dictionary/shipping_methods
1636
     *
1637
     * @return array List of shipping methods
1638
     *
1639
     * @throws  RestException   400     Bad value for sqlfilters
1640
     * @throws  RestException   503     Error when retrieving list of shipping modes
1641
     */
1642
    public function getShippingModes($limit = 100, $page = 0, $active = 1, $lang = '', $sqlfilters = '')
1643
    {
1644
        $list = array();
1645
1646
        $sql = "SELECT rowid as id, code, libelle as label, description, tracking, module";
1647
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_shipment_mode as t";
1648
        $sql .= " WHERE t.entity IN (" . getEntity('c_shipment_mode') . ")";
1649
        $sql .= " AND t.active = " . ((int) $active);
1650
        // Add sql filters
1651
        if ($sqlfilters) {
1652
            $errormessage = '';
1653
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
1654
            if ($errormessage) {
1655
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
1656
            }
1657
        }
1658
1659
1660
        //$sql.= $this->db->order($sortfield, $sortorder);
1661
1662
        if ($limit) {
1663
            if ($page < 0) {
1664
                $page = 0;
1665
            }
1666
            $offset = $limit * $page;
1667
1668
            $sql .= $this->db->plimit($limit, $offset);
1669
        }
1670
1671
        $result = $this->db->query($sql);
1672
1673
        if ($result) {
1674
            $num = $this->db->num_rows($result);
1675
            $min = min($num, ($limit <= 0 ? $num : $limit));
1676
            for ($i = 0; $i < $min; $i++) {
1677
                $method = $this->db->fetch_object($result);
1678
                $this->translateLabel($method, $lang, '', array('dict'));
1679
                $list[] = $method;
1680
            }
1681
        } else {
1682
            throw new RestException(503, $this->db->lasterror());
1683
        }
1684
1685
        return $list;
1686
    }
1687
1688
    /**
1689
     * Get the list of measuring units.
1690
     *
1691
     * @param string    $sortfield  Sort field
1692
     * @param string    $sortorder  Sort order
1693
     * @param int       $limit      Number of items per page
1694
     * @param int       $page       Page number (starting from zero)
1695
     * @param int       $active     Measuring unit is active or not {@min 0} {@max 1}
1696
     * @param string    $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)"
1697
     * @return array                List of measuring unit
1698
     *
1699
     * @url     GET dictionary/units
1700
     *
1701
     * @throws  RestException   400     Bad value for sqlfilters
1702
     * @throws  RestException   503     Error when retrieving list of measuring units
1703
     */
1704
    public function getListOfMeasuringUnits($sortfield = "rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '')
1705
    {
1706
        $list = array();
1707
1708
        $sql = "SELECT t.rowid, t.code, t.label,t.short_label, t.active, t.scale, t.unit_type";
1709
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_units as t";
1710
        $sql .= " WHERE t.active = " . ((int) $active);
1711
        // Add sql filters
1712
        if ($sqlfilters) {
1713
            $errormessage = '';
1714
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
1715
            if ($errormessage) {
1716
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
1717
            }
1718
        }
1719
1720
1721
        $sql .= $this->db->order($sortfield, $sortorder);
1722
1723
        if ($limit) {
1724
            if ($page < 0) {
1725
                $page = 0;
1726
            }
1727
            $offset = $limit * $page;
1728
1729
            $sql .= $this->db->plimit($limit, $offset);
1730
        }
1731
1732
        $result = $this->db->query($sql);
1733
1734
        if ($result) {
1735
            $num = $this->db->num_rows($result);
1736
            $min = min($num, ($limit <= 0 ? $num : $limit));
1737
            for ($i = 0; $i < $min; $i++) {
1738
                $list[] = $this->db->fetch_object($result);
1739
            }
1740
        } else {
1741
            throw new RestException(503, 'Error when retrieving list of measuring units: ' . $this->db->lasterror());
1742
        }
1743
1744
        return $list;
1745
    }
1746
1747
    /**
1748
     * Get the list of legal form of business.
1749
     *
1750
     * @param string    $sortfield  Sort field
1751
     * @param string    $sortorder  Sort order
1752
     * @param int       $limit      Number of items per page
1753
     * @param int       $page       Page number (starting from zero)
1754
     * @param int       $country    To filter on country
1755
     * @param int       $active     Lega form is active or not {@min 0} {@max 1}
1756
     * @param string    $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)"
1757
     * @return array                List of legal form
1758
     *
1759
     * @url     GET dictionary/legal_form
1760
     *
1761
     * @throws  RestException   400     Bad value for sqlfilters
1762
     * @throws  RestException   503     Error when retrieving list of legal form
1763
     */
1764
    public function getListOfLegalForm($sortfield = "rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $country = 0, $active = 1, $sqlfilters = '')
1765
    {
1766
        $list = array();
1767
1768
        $sql = "SELECT t.rowid, t.code, t.fk_pays, t.libelle, t.isvatexempted, t.active, t.module, t.position";
1769
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_forme_juridique as t";
1770
        $sql .= " WHERE t.active = " . ((int) $active);
1771
        if ($country) {
1772
            $sql .= " AND t.fk_pays = " . ((int) $country);
1773
        }
1774
        // Add sql filters
1775
        if ($sqlfilters) {
1776
            $errormessage = '';
1777
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
1778
            if ($errormessage) {
1779
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
1780
            }
1781
        }
1782
1783
1784
        $sql .= $this->db->order($sortfield, $sortorder);
1785
1786
        if ($limit) {
1787
            if ($page < 0) {
1788
                $page = 0;
1789
            }
1790
            $offset = $limit * $page;
1791
1792
            $sql .= $this->db->plimit($limit, $offset);
1793
        }
1794
1795
        $result = $this->db->query($sql);
1796
1797
        if ($result) {
1798
            $num = $this->db->num_rows($result);
1799
            $min = min($num, ($limit <= 0 ? $num : $limit));
1800
            for ($i = 0; $i < $min; $i++) {
1801
                $list[] = $this->db->fetch_object($result);
1802
            }
1803
        } else {
1804
            throw new RestException(503, 'Error when retrieving list of legal form: ' . $this->db->lasterror());
1805
        }
1806
1807
        return $list;
1808
    }
1809
1810
    /**
1811
     * Get the list of staff.
1812
     *
1813
     * @param string    $sortfield  Sort field
1814
     * @param string    $sortorder  Sort order
1815
     * @param int       $limit      Number of items per page
1816
     * @param int       $page       Page number (starting from zero)
1817
     * @param int       $active     Staff is active or not {@min 0} {@max 1}
1818
     * @param string    $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)"
1819
     * @return array                List of staff
1820
     *
1821
     * @url     GET dictionary/staff
1822
     *
1823
     * @throws  RestException   400     Bad value for sqlfilters
1824
     * @throws  RestException   503     Error when retrieving list of staff
1825
     */
1826
    public function getListOfStaff($sortfield = "id", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '')
1827
    {
1828
        $list = array();
1829
1830
        $sql = "SELECT t.id, t.code, t.libelle, t.active, t.module";
1831
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_effectif as t";
1832
        $sql .= " WHERE t.active = " . ((int) $active);
1833
        // Add sql filters
1834
        if ($sqlfilters) {
1835
            $errormessage = '';
1836
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
1837
            if ($errormessage) {
1838
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
1839
            }
1840
        }
1841
1842
1843
        $sql .= $this->db->order($sortfield, $sortorder);
1844
1845
        if ($limit) {
1846
            if ($page < 0) {
1847
                $page = 0;
1848
            }
1849
            $offset = $limit * $page;
1850
1851
            $sql .= $this->db->plimit($limit, $offset);
1852
        }
1853
1854
        $result = $this->db->query($sql);
1855
1856
        if ($result) {
1857
            $num = $this->db->num_rows($result);
1858
            $min = min($num, ($limit <= 0 ? $num : $limit));
1859
            for ($i = 0; $i < $min; $i++) {
1860
                $list[] = $this->db->fetch_object($result);
1861
            }
1862
        } else {
1863
            throw new RestException(503, 'Error when retrieving list of staff: ' . $this->db->lasterror());
1864
        }
1865
1866
        return $list;
1867
    }
1868
1869
    /**
1870
     * Get the list of social networks.
1871
     *
1872
     * @param string    $sortfield  Sort field
1873
     * @param string    $sortorder  Sort order
1874
     * @param int       $limit      Number of items per page
1875
     * @param int       $page       Page number (starting from zero)
1876
     * @param int       $active     Social network is active or not {@min 0} {@max 1}
1877
     * @param string    $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)"
1878
     * @return array                List of social networks
1879
     *
1880
     * @url     GET dictionary/socialnetworks
1881
     *
1882
     * @throws  RestException   400     Bad value for sqlfilters
1883
     * @throws  RestException   503     Error when retrieving list of social networks
1884
     */
1885
    public function getListOfsocialNetworks($sortfield = "rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $sqlfilters = '')
1886
    {
1887
        global $conf;
1888
1889
        if (!isModEnabled('socialnetworks')) {
1890
            throw new RestException(400, 'API not available: this dictionary is not enabled by setup');
1891
        }
1892
1893
        $list = array();
1894
        //TODO link with multicurrency module
1895
        $sql = "SELECT t.rowid, t.entity, t.code, t.label, t.url, t.icon, t.active";
1896
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_socialnetworks as t";
1897
        $sql .= " WHERE t.entity IN (" . getEntity('c_socialnetworks') . ")";
1898
        $sql .= " AND t.active = " . ((int) $active);
1899
        // Add sql filters
1900
        if ($sqlfilters) {
1901
            $errormessage = '';
1902
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
1903
            if ($errormessage) {
1904
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
1905
            }
1906
        }
1907
1908
1909
        $sql .= $this->db->order($sortfield, $sortorder);
1910
1911
        if ($limit) {
1912
            if ($page < 0) {
1913
                $page = 0;
1914
            }
1915
            $offset = $limit * $page;
1916
1917
            $sql .= $this->db->plimit($limit, $offset);
1918
        }
1919
1920
        $result = $this->db->query($sql);
1921
1922
        if ($result) {
1923
            $num = $this->db->num_rows($result);
1924
            $min = min($num, ($limit <= 0 ? $num : $limit));
1925
            for ($i = 0; $i < $min; $i++) {
1926
                $list[] = $this->db->fetch_object($result);
1927
            }
1928
        } else {
1929
            throw new RestException(503, 'Error when retrieving list of social networks: ' . $this->db->lasterror());
1930
        }
1931
1932
        return $list;
1933
    }
1934
1935
    /**
1936
     * Get the list of tickets categories.
1937
     *
1938
     * @param string    $sortfield  Sort field
1939
     * @param string    $sortorder  Sort order
1940
     * @param int       $limit      Number of items per page
1941
     * @param int       $page       Page number (starting from zero)
1942
     * @param int       $active     Payment term is active or not {@min 0} {@max 1}
1943
     * @param string    $lang       Code of the language the label of the category must be translated to
1944
     * @param string    $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)"
1945
     * @return array                List of ticket categories
1946
     *
1947
     * @url     GET dictionary/ticket_categories
1948
     *
1949
     * @throws  RestException   400     Bad value for sqlfilters
1950
     * @throws  RestException   503     Error when retrieving list of tickets categories
1951
     */
1952
    public function getTicketsCategories($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $lang = '', $sqlfilters = '')
1953
    {
1954
        $list = array();
1955
1956
        $sql = "SELECT rowid, code, pos,  label, use_default, description";
1957
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_ticket_category as t";
1958
        $sql .= " WHERE t.entity IN (" . getEntity('c_ticket_category') . ")";
1959
        $sql .= " AND t.active = " . ((int) $active);
1960
        // Add sql filters
1961
        if ($sqlfilters) {
1962
            $errormessage = '';
1963
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
1964
            if ($errormessage) {
1965
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
1966
            }
1967
        }
1968
1969
1970
        $sql .= $this->db->order($sortfield, $sortorder);
1971
1972
        if ($limit) {
1973
            if ($page < 0) {
1974
                $page = 0;
1975
            }
1976
            $offset = $limit * $page;
1977
1978
            $sql .= $this->db->plimit($limit, $offset);
1979
        }
1980
1981
        $result = $this->db->query($sql);
1982
1983
        if ($result) {
1984
            $num = $this->db->num_rows($result);
1985
            $min = min($num, ($limit <= 0 ? $num : $limit));
1986
            for ($i = 0; $i < $min; $i++) {
1987
                $category = $this->db->fetch_object($result);
1988
                $this->translateLabel($category, $lang, 'TicketCategoryShort', array('ticket'));
1989
                $list[] = $category;
1990
            }
1991
        } else {
1992
            throw new RestException(503, 'Error when retrieving list of ticket categories : ' . $this->db->lasterror());
1993
        }
1994
1995
        return $list;
1996
    }
1997
1998
    /**
1999
     * Get the list of tickets severity.
2000
     *
2001
     * @param string    $sortfield  Sort field
2002
     * @param string    $sortorder  Sort order
2003
     * @param int       $limit      Number of items per page
2004
     * @param int       $page       Page number (starting from zero)
2005
     * @param int       $active     Payment term is active or not {@min 0} {@max 1}
2006
     * @param string    $lang       Code of the language the label of the severity must be translated to
2007
     * @param string    $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)"
2008
     * @return array                List of ticket severities
2009
     *
2010
     * @url     GET dictionary/ticket_severities
2011
     *
2012
     * @throws  RestException   400     Bad value for sqlfilters
2013
     * @throws  RestException   503     Error when retrieving list of tickets severities
2014
     */
2015
    public function getTicketsSeverities($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $lang = '', $sqlfilters = '')
2016
    {
2017
        $list = array();
2018
2019
        $sql = "SELECT rowid, code, pos,  label, use_default, color, description";
2020
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_ticket_severity as t";
2021
        $sql .= " WHERE t.entity IN (" . getEntity('c_ticket_severity') . ")";
2022
        $sql .= " AND t.active = " . ((int) $active);
2023
        // Add sql filters
2024
        if ($sqlfilters) {
2025
            $errormessage = '';
2026
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
2027
            if ($errormessage) {
2028
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
2029
            }
2030
        }
2031
2032
2033
        $sql .= $this->db->order($sortfield, $sortorder);
2034
2035
        if ($limit) {
2036
            if ($page < 0) {
2037
                $page = 0;
2038
            }
2039
            $offset = $limit * $page;
2040
2041
            $sql .= $this->db->plimit($limit, $offset);
2042
        }
2043
2044
        $result = $this->db->query($sql);
2045
2046
        if ($result) {
2047
            $num = $this->db->num_rows($result);
2048
            $min = min($num, ($limit <= 0 ? $num : $limit));
2049
            for ($i = 0; $i < $min; $i++) {
2050
                $severity = $this->db->fetch_object($result);
2051
                $this->translateLabel($severity, $lang, 'TicketSeverityShort', array('ticket'));
2052
                $list[] = $severity;
2053
            }
2054
        } else {
2055
            throw new RestException(503, 'Error when retrieving list of ticket severities : ' . $this->db->lasterror());
2056
        }
2057
2058
        return $list;
2059
    }
2060
2061
    /**
2062
     * Get the list of tickets types.
2063
     *
2064
     * @param string    $sortfield  Sort field
2065
     * @param string    $sortorder  Sort order
2066
     * @param int       $limit      Number of items per page
2067
     * @param int       $page       Page number (starting from zero)
2068
     * @param int       $active     Payment term is active or not {@min 0} {@max 1}
2069
     * @param string    $lang       Code of the language the label of the type must be translated to
2070
     * @param string    $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)"
2071
     * @return array                List of ticket types
2072
     *
2073
     * @url     GET dictionary/ticket_types
2074
     *
2075
     * @throws RestException 400 Bad value for sqlfilters
2076
     * @throws RestException 503 Error when retrieving list of tickets types
2077
     */
2078
    public function getTicketsTypes($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $lang = '', $sqlfilters = '')
2079
    {
2080
        $list = array();
2081
2082
        $sql = "SELECT rowid, code, pos,  label, use_default, description";
2083
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_ticket_type as t";
2084
        $sql .= " WHERE t.entity IN (" . getEntity('c_ticket_type') . ")";
2085
        $sql .= " AND t.active = " . ((int) $active);
2086
2087
        // Add sql filters
2088
        if ($sqlfilters) {
2089
            $errormessage = '';
2090
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
2091
            if ($errormessage) {
2092
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
2093
            }
2094
        }
2095
2096
2097
        $sql .= $this->db->order($sortfield, $sortorder);
2098
2099
        if ($limit) {
2100
            if ($page < 0) {
2101
                $page = 0;
2102
            }
2103
            $offset = $limit * $page;
2104
2105
            $sql .= $this->db->plimit($limit, $offset);
2106
        }
2107
2108
        $result = $this->db->query($sql);
2109
2110
        if ($result) {
2111
            $num = $this->db->num_rows($result);
2112
            $min = min($num, ($limit <= 0 ? $num : $limit));
2113
            for ($i = 0; $i < $min; $i++) {
2114
                $type = $this->db->fetch_object($result);
2115
                $this->translateLabel($type, $lang, 'TicketTypeShort', array('ticket'));
2116
                $list[] = $type;
2117
            }
2118
        } else {
2119
            throw new RestException(503, 'Error when retrieving list of ticket types : ' . $this->db->lasterror());
2120
        }
2121
2122
        return $list;
2123
    }
2124
2125
    /**
2126
     * Get the list of incoterms.
2127
     *
2128
     * @param string    $sortfield  Sort field
2129
     * @param string    $sortorder  Sort order
2130
     * @param int       $limit      Number of items per page
2131
     * @param int       $page       Page number (starting from zero)
2132
     * @param int       $active     Payment term is active or not {@min 0} {@max 1}
2133
     * @param string    $lang       Code of the language the label of the type must be translated to
2134
     * @param string    $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)"
2135
     * @return array                List of incoterm types
2136
     *
2137
     * @url     GET dictionary/incoterms
2138
     *
2139
     * @throws RestException 503 Error when retrieving list of incoterms types
2140
     */
2141
    public function getListOfIncoterms($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $active = 1, $lang = '', $sqlfilters = '')
2142
    {
2143
        $list = array();
2144
2145
        $sql = "SELECT rowid, code, active";
2146
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_incoterms as t";
2147
        $sql .= " WHERE 1=1";
2148
2149
        // Add sql filters
2150
        if ($sqlfilters) {
2151
            $errormessage = '';
2152
            if (!DolibarrApi::_checkFilters($sqlfilters, $errormessage)) {
2153
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
2154
            }
2155
            $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)';
2156
            $sql .= " AND (" . preg_replace_callback('/' . $regexstring . '/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters) . ")";
2157
        }
2158
2159
2160
        $sql .= $this->db->order($sortfield, $sortorder);
2161
2162
        if ($limit) {
2163
            if ($page < 0) {
2164
                $page = 0;
2165
            }
2166
            $offset = $limit * $page;
2167
2168
            $sql .= $this->db->plimit($limit, $offset);
2169
        }
2170
2171
        $result = $this->db->query($sql);
2172
2173
        if ($result) {
2174
            $num = $this->db->num_rows($result);
2175
            $min = min($num, ($limit <= 0 ? $num : $limit));
2176
            for ($i = 0; $i < $min; $i++) {
2177
                $type = $this->db->fetch_object($result);
2178
                $list[] = $type;
2179
            }
2180
        } else {
2181
            throw new RestException(503, 'Error when retrieving list of incoterm types : ' . $this->db->lasterror());
2182
        }
2183
2184
        return $list;
2185
    }
2186
2187
    /**
2188
     * Get properties of company
2189
     *
2190
     * @url GET /company
2191
     *
2192
     * @return  array|mixed Mysoc object
2193
     *
2194
     * @throws RestException 403 Forbidden
2195
     */
2196
    public function getCompany()
2197
    {
2198
        global $conf, $mysoc;
2199
2200
        if (
2201
            !DolibarrApiAccess::$user->admin
2202
            && (!getDolGlobalString('API_LOGINS_ALLOWED_FOR_GET_COMPANY') || DolibarrApiAccess::$user->login != $conf->global->API_LOGINS_ALLOWED_FOR_GET_COMPANY)
2203
        ) {
2204
            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');
2205
        }
2206
2207
        unset($mysoc->pays);
2208
        unset($mysoc->note);
2209
        unset($mysoc->nom);
2210
2211
        unset($mysoc->lines);
2212
2213
        unset($mysoc->effectif);
2214
        unset($mysoc->effectif_id);
2215
        unset($mysoc->forme_juridique_code);
2216
        unset($mysoc->forme_juridique);
2217
        unset($mysoc->mode_reglement_supplier_id);
2218
        unset($mysoc->cond_reglement_supplier_id);
2219
        unset($mysoc->transport_mode_supplier_id);
2220
        unset($mysoc->fk_prospectlevel);
2221
2222
        unset($mysoc->total_ht);
2223
        unset($mysoc->total_tva);
2224
        unset($mysoc->total_localtax1);
2225
        unset($mysoc->total_localtax2);
2226
        unset($mysoc->total_ttc);
2227
2228
        unset($mysoc->lastname);
2229
        unset($mysoc->firstname);
2230
        unset($mysoc->civility_id);
2231
2232
        unset($mysoc->client);
2233
        unset($mysoc->prospect);
2234
        unset($mysoc->fournisseur);
2235
        unset($mysoc->contact_id);
2236
2237
        unset($mysoc->fk_incoterms);
2238
        unset($mysoc->label_incoterms);
2239
        unset($mysoc->location_incoterms);
2240
2241
        return $this->_cleanObjectDatas($mysoc);
2242
    }
2243
2244
    /**
2245
     * Get the list of establishments.
2246
     *
2247
     * @return array                List of establishments
2248
     *
2249
     * @url     GET /establishments
2250
     *
2251
     * @throws RestException 503 Error when retrieving list of establishments
2252
     */
2253
    public function getEstablishments()
2254
    {
2255
        $list = array();
2256
2257
        $limit = 0;
2258
2259
        $sql = "SELECT e.rowid, e.rowid as ref, e.label, e.address, e.zip, e.town, e.status";
2260
        $sql .= " FROM " . MAIN_DB_PREFIX . "establishment as e";
2261
        $sql .= " WHERE e.entity IN (" . getEntity('establishment') . ')';
2262
        // if ($type) $sql .= " AND t.type LIKE '%".$this->db->escape($type)."%'";
2263
        // if ($module)    $sql .= " AND t.module LIKE '%".$this->db->escape($module)."%'";
2264
        // Add sql filters
2265
2266
        $result = $this->db->query($sql);
2267
2268
        if ($result) {
2269
            $num = $this->db->num_rows($result);
2270
            $min = min($num, ($limit <= 0 ? $num : $limit));
2271
            for ($i = 0; $i < $min; $i++) {
2272
                $list[] = $this->db->fetch_object($result);
2273
            }
2274
        } else {
2275
            throw new RestException(503, 'Error when retrieving list of establishments : ' . $this->db->lasterror());
2276
        }
2277
2278
        return $list;
2279
    }
2280
2281
    /**
2282
     * Get establishment by ID.
2283
     *
2284
     * @param   int       $id           ID of establishment
2285
     * @return  array                  Object with cleaned properties
2286
     *
2287
     * @url     GET establishments/{id}
2288
     *
2289
     * @throws RestException 404 Establishment not found
2290
     * @throws RestException 503 Error when retrieving establishment
2291
     */
2292
    public function getEtablishmentByID($id)
2293
    {
2294
        $establishment = new Establishment($this->db);
2295
2296
        $result = $establishment->fetch($id);
2297
        if ($result < 0) {
2298
            throw new RestException(503, 'Error when retrieving establishment : ' . $establishment->error);
2299
        } elseif ($result == 0) {
2300
            throw new RestException(404, 'Establishment not found');
2301
        }
2302
2303
        return $this->_cleanObjectDatas($establishment);
2304
    }
2305
2306
    /**
2307
     * Get value of a setup variables
2308
     *
2309
     * Note that conf variables that stores security key or password hashes can't be loaded with API.
2310
     *
2311
     * @param   string          $constantname   Name of conf variable to get
2312
     * @return  string                          Data without useless information
2313
     *
2314
     * @url     GET conf/{constantname}
2315
     *
2316
     * @throws RestException 400 Error Bad or unknown value for constantname
2317
     * @throws RestException 403 Forbidden
2318
     */
2319
    public function getConf($constantname)
2320
    {
2321
        global $conf;
2322
2323
        if (
2324
            !DolibarrApiAccess::$user->admin
2325
            && (!getDolGlobalString('API_LOGINS_ALLOWED_FOR_CONST_READ') || DolibarrApiAccess::$user->login != getDolGlobalString('API_LOGINS_ALLOWED_FOR_CONST_READ'))
2326
        ) {
2327
            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');
2328
        }
2329
2330
        if (!preg_match('/^[a-zA-Z0-9_]+$/', $constantname) || !isset($conf->global->$constantname)) {
2331
            throw new RestException(400, 'Error Bad or unknown value for constantname');
2332
        }
2333
        if (isASecretKey($constantname)) {
2334
            throw new RestException(403, 'Forbidden. This parameter can not be read with APIs');
2335
        }
2336
2337
        return getDolGlobalString($constantname);
2338
    }
2339
2340
    /**
2341
     * Do a test of integrity for files and setup.
2342
     *
2343
     * @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.
2344
     * @return array                    Result of file and setup integrity check
2345
     *
2346
     * @url     GET checkintegrity
2347
     *
2348
     * @throws RestException 403 Forbidden
2349
     * @throws RestException 404 Signature file not found
2350
     * @throws RestException 500 Technical error
2351
     */
2352
    public function getCheckIntegrity($target)
2353
    {
2354
        global $langs, $conf;
2355
2356
        if (
2357
            !DolibarrApiAccess::$user->admin
2358
            && (!getDolGlobalString('API_LOGINS_ALLOWED_FOR_INTEGRITY_CHECK') || DolibarrApiAccess::$user->login != $conf->global->API_LOGINS_ALLOWED_FOR_INTEGRITY_CHECK)
2359
        ) {
2360
            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');
2361
        }
2362
2363
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
2364
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/geturl.lib.php';
2365
2366
        $langs->load("admin");
2367
2368
        $outexpectedchecksum = '';
2369
        $outcurrentchecksum = '';
2370
2371
        // Modified or missing files
2372
        $file_list = array('missing' => array(), 'updated' => array());
2373
2374
        // Local file to compare to
2375
        $xmlshortfile = dol_sanitizeFileName('filelist-' . DOL_VERSION . getDolGlobalString('MAIN_FILECHECK_LOCAL_SUFFIX') . '.xml' . getDolGlobalString('MAIN_FILECHECK_LOCAL_EXT'));
2376
2377
        $xmlfile = DOL_DOCUMENT_ROOT . '/install/' . $xmlshortfile;
2378
        if (!preg_match('/\.zip$/i', $xmlfile) && dol_is_file($xmlfile . '.zip')) {
2379
            $xmlfile = $xmlfile . '.zip';
2380
        }
2381
2382
        // Remote file to compare to
2383
        $xmlremote = (($target == 'default' || $target == 'local') ? '' : $target);
2384
        if (empty($xmlremote) && getDolGlobalString('MAIN_FILECHECK_URL')) {
2385
            $xmlremote = getDolGlobalString('MAIN_FILECHECK_URL');
2386
        }
2387
        $param = 'MAIN_FILECHECK_URL_' . DOL_VERSION;
2388
        if (empty($xmlremote) && getDolGlobalString($param)) {
2389
            $xmlremote = getDolGlobalString($param);
2390
        }
2391
        if (empty($xmlremote)) {
2392
            $xmlremote = 'https://www.dolibarr.org/files/stable/signatures/filelist-' . DOL_VERSION . '.xml';
2393
        }
2394
        if ($xmlremote && !preg_match('/^https?:\/\//i', $xmlremote)) {
2395
            $langs->load("errors");
2396
            throw new RestException(500, $langs->trans("ErrorURLMustStartWithHttp", $xmlremote));
2397
        }
2398
        if ($xmlremote && !preg_match('/\.xml$/', $xmlremote)) {
2399
            $langs->load("errors");
2400
            throw new RestException(500, $langs->trans("ErrorURLMustEndWith", $xmlremote, '.xml'));
2401
        }
2402
2403
        if (LIBXML_VERSION < 20900) {
2404
            // Avoid load of external entities (security problem).
2405
            // Required only if LIBXML_VERSION < 20900
2406
            // @phan-suppress-next-line PhanDeprecatedFunctionInternal
2407
            libxml_disable_entity_loader(true);
2408
        }
2409
2410
        if ($target == 'local') {
2411
            if (dol_is_file($xmlfile)) {
2412
                $xml = simplexml_load_file($xmlfile);
2413
            } else {
2414
                throw new RestException(500, $langs->trans('XmlNotFound') . ': /install/' . $xmlshortfile);
2415
            }
2416
        } else {
2417
            $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.
2418
2419
            // Return array('content'=>response,'curl_error_no'=>errno,'curl_error_msg'=>errmsg...)
2420
            if (!$xmlarray['curl_error_no'] && $xmlarray['http_code'] != '400' && $xmlarray['http_code'] != '404') {
2421
                $xmlfile = $xmlarray['content'];
2422
                //print "xmlfilestart".$xmlfile."endxmlfile";
2423
                $xml = simplexml_load_string($xmlfile, 'SimpleXMLElement', LIBXML_NOCDATA | LIBXML_NONET);
2424
            } else {
2425
                $errormsg = $langs->trans('XmlNotFound') . ': ' . $xmlremote . ' - ' . $xmlarray['http_code'] . (($xmlarray['http_code'] == 400 && $xmlarray['content']) ? ' ' . $xmlarray['content'] : '') . ' ' . $xmlarray['curl_error_no'] . ' ' . $xmlarray['curl_error_msg'];
2426
                throw new RestException(500, $errormsg);
2427
            }
2428
        }
2429
2430
        if ($xml) {
2431
            $checksumconcat = array();
2432
            $file_list = array();
2433
            $out = '';
2434
2435
            // Forced constants
2436
            if (is_object($xml->dolibarr_constants[0])) {
2437
                $out .= load_fiche_titre($langs->trans("ForcedConstants"));
2438
2439
                $out .= '<div class="div-table-responsive-no-min">';
2440
                $out .= '<table class="noborder">';
2441
                $out .= '<tr class="liste_titre">';
2442
                $out .= '<td>#</td>';
2443
                $out .= '<td>' . $langs->trans("Constant") . '</td>';
2444
                $out .= '<td class="center">' . $langs->trans("ExpectedValue") . '</td>';
2445
                $out .= '<td class="center">' . $langs->trans("Value") . '</td>';
2446
                $out .= '</tr>' . "\n";
2447
2448
                $i = 0;
2449
                foreach ($xml->dolibarr_constants[0]->constant as $constant) {    // $constant is a simpleXMLElement
2450
                    $constname = $constant['name'];
2451
                    $constvalue = (string) $constant;
2452
                    $constvalue = (empty($constvalue) ? '0' : $constvalue);
2453
                    // Value found
2454
                    $value = '';
2455
                    if ($constname && getDolGlobalString($constname) != '') {
2456
                        $value = getDolGlobalString($constname);
2457
                    }
2458
                    $valueforchecksum = (empty($value) ? '0' : $value);
2459
2460
                    $checksumconcat[] = $valueforchecksum;
2461
2462
                    $i++;
2463
                    $out .= '<tr class="oddeven">';
2464
                    $out .= '<td>' . $i . '</td>' . "\n";
2465
                    $out .= '<td>' . dol_escape_htmltag($constname) . '</td>' . "\n";
2466
                    $out .= '<td class="center">' . dol_escape_htmltag($constvalue) . '</td>' . "\n";
2467
                    $out .= '<td class="center">' . dol_escape_htmltag($valueforchecksum) . '</td>' . "\n";
2468
                    $out .= "</tr>\n";
2469
                }
2470
2471
                if ($i == 0) {
2472
                    $out .= '<tr class="oddeven"><td colspan="4" class="opacitymedium">' . $langs->trans("None") . '</td></tr>';
2473
                }
2474
                $out .= '</table>';
2475
                $out .= '</div>';
2476
2477
                $out .= '<br>';
2478
            }
2479
2480
            // Scan htdocs
2481
            if (is_object($xml->dolibarr_htdocs_dir[0])) {
2482
                $includecustom = (empty($xml->dolibarr_htdocs_dir[0]['includecustom']) ? 0 : $xml->dolibarr_htdocs_dir[0]['includecustom']);
2483
2484
                // Define qualified files (must be same than into generate_filelist_xml.php and in api_setup.class.php)
2485
                $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)$';
2486
                $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
2487
                $scanfiles = dol_dir_list(DOL_DOCUMENT_ROOT, 'files', 1, $regextoinclude, $regextoexclude);
2488
2489
                // Fill file_list with files in signature, new files, modified files
2490
                $ret = getFilesUpdated($file_list, $xml->dolibarr_htdocs_dir[0], '', DOL_DOCUMENT_ROOT, $checksumconcat); // Fill array $file_list
2491
                // Complete with list of new files
2492
                foreach ($scanfiles as $keyfile => $valfile) {
2493
                    $tmprelativefilename = preg_replace('/^' . preg_quote(DOL_DOCUMENT_ROOT, '/') . '/', '', $valfile['fullname']);
2494
                    if (!in_array($tmprelativefilename, $file_list['insignature'])) {
2495
                        $md5newfile = @md5_file($valfile['fullname']); // Can fails if we don't have permission to open/read file
2496
                        $file_list['added'][] = array('filename' => $tmprelativefilename, 'md5' => $md5newfile);
2497
                    }
2498
                }
2499
2500
                // Files missing
2501
                $out .= load_fiche_titre($langs->trans("FilesMissing"));
2502
2503
                $out .= '<div class="div-table-responsive-no-min">';
2504
                $out .= '<table class="noborder">';
2505
                $out .= '<tr class="liste_titre">';
2506
                $out .= '<td>#</td>';
2507
                $out .= '<td>' . $langs->trans("Filename") . '</td>';
2508
                $out .= '<td class="center">' . $langs->trans("ExpectedChecksum") . '</td>';
2509
                $out .= '</tr>' . "\n";
2510
                $tmpfilelist = dol_sort_array($file_list['missing'], 'filename');
2511
                if (is_array($tmpfilelist) && count($tmpfilelist)) {
2512
                    $i = 0;
2513
                    foreach ($tmpfilelist as $file) {
2514
                        $i++;
2515
                        $out .= '<tr class="oddeven">';
2516
                        $out .= '<td>' . $i . '</td>' . "\n";
2517
                        $out .= '<td>' . dol_escape_htmltag($file['filename']) . '</td>' . "\n";
2518
                        $out .= '<td class="center">' . $file['expectedmd5'] . '</td>' . "\n";
2519
                        $out .= "</tr>\n";
2520
                    }
2521
                } else {
2522
                    $out .= '<tr class="oddeven"><td colspan="3" class="opacitymedium">' . $langs->trans("None") . '</td></tr>';
2523
                }
2524
                $out .= '</table>';
2525
                $out .= '</div>';
2526
2527
                $out .= '<br>';
2528
2529
                // Files modified
2530
                $out .= load_fiche_titre($langs->trans("FilesModified"));
2531
2532
                $totalsize = 0;
2533
                $out .= '<div class="div-table-responsive-no-min">';
2534
                $out .= '<table class="noborder">';
2535
                $out .= '<tr class="liste_titre">';
2536
                $out .= '<td>#</td>';
2537
                $out .= '<td>' . $langs->trans("Filename") . '</td>';
2538
                $out .= '<td class="center">' . $langs->trans("ExpectedChecksum") . '</td>';
2539
                $out .= '<td class="center">' . $langs->trans("CurrentChecksum") . '</td>';
2540
                $out .= '<td class="right">' . $langs->trans("Size") . '</td>';
2541
                $out .= '<td class="right">' . $langs->trans("DateModification") . '</td>';
2542
                $out .= '</tr>' . "\n";
2543
                $tmpfilelist2 = dol_sort_array($file_list['updated'], 'filename');
2544
                if (is_array($tmpfilelist2) && count($tmpfilelist2)) {
2545
                    $i = 0;
2546
                    foreach ($tmpfilelist2 as $file) {
2547
                        $i++;
2548
                        $out .= '<tr class="oddeven">';
2549
                        $out .= '<td>' . $i . '</td>' . "\n";
2550
                        $out .= '<td>' . dol_escape_htmltag($file['filename']) . '</td>' . "\n";
2551
                        $out .= '<td class="center">' . $file['expectedmd5'] . '</td>' . "\n";
2552
                        $out .= '<td class="center">' . $file['md5'] . '</td>' . "\n";
2553
                        $size = dol_filesize(DOL_DOCUMENT_ROOT . '/' . $file['filename']);
2554
                        $totalsize += $size;
2555
                        $out .= '<td class="right">' . dol_print_size($size) . '</td>' . "\n";
2556
                        $out .= '<td class="right">' . dol_print_date(dol_filemtime(DOL_DOCUMENT_ROOT . '/' . $file['filename']), 'dayhour') . '</td>' . "\n";
2557
                        $out .= "</tr>\n";
2558
                    }
2559
                    $out .= '<tr class="liste_total">';
2560
                    $out .= '<td></td>' . "\n";
2561
                    $out .= '<td>' . $langs->trans("Total") . '</td>' . "\n";
2562
                    $out .= '<td align="center"></td>' . "\n";
2563
                    $out .= '<td align="center"></td>' . "\n";
2564
                    $out .= '<td class="right">' . dol_print_size($totalsize) . '</td>' . "\n";
2565
                    $out .= '<td class="right"></td>' . "\n";
2566
                    $out .= "</tr>\n";
2567
                } else {
2568
                    $out .= '<tr class="oddeven"><td colspan="5" class="opacitymedium">' . $langs->trans("None") . '</td></tr>';
2569
                }
2570
                $out .= '</table>';
2571
                $out .= '</div>';
2572
2573
                $out .= '<br>';
2574
2575
                // Files added
2576
                $out .= load_fiche_titre($langs->trans("FilesAdded"));
2577
2578
                $totalsize = 0;
2579
                $out .= '<div class="div-table-responsive-no-min">';
2580
                $out .= '<table class="noborder">';
2581
                $out .= '<tr class="liste_titre">';
2582
                $out .= '<td>#</td>';
2583
                $out .= '<td>' . $langs->trans("Filename") . '</td>';
2584
                $out .= '<td class="center">' . $langs->trans("ExpectedChecksum") . '</td>';
2585
                $out .= '<td class="center">' . $langs->trans("CurrentChecksum") . '</td>';
2586
                $out .= '<td class="right">' . $langs->trans("Size") . '</td>';
2587
                $out .= '<td class="right">' . $langs->trans("DateModification") . '</td>';
2588
                $out .= '</tr>' . "\n";
2589
                $tmpfilelist3 = dol_sort_array($file_list['added'], 'filename');
2590
                if (is_array($tmpfilelist3) && count($tmpfilelist3)) {
2591
                    $i = 0;
2592
                    foreach ($tmpfilelist3 as $file) {
2593
                        $i++;
2594
                        $out .= '<tr class="oddeven">';
2595
                        $out .= '<td>' . $i . '</td>' . "\n";
2596
                        $out .= '<td>' . dol_escape_htmltag($file['filename']) . '</td>' . "\n";
2597
                        $out .= '<td class="center">' . $file['expectedmd5'] . '</td>' . "\n";
2598
                        $out .= '<td class="center">' . $file['md5'] . '</td>' . "\n";
2599
                        $size = dol_filesize(DOL_DOCUMENT_ROOT . '/' . $file['filename']);
2600
                        $totalsize += $size;
2601
                        $out .= '<td class="right">' . dol_print_size($size) . '</td>' . "\n";
2602
                        $out .= '<td class="right">' . dol_print_date(dol_filemtime(DOL_DOCUMENT_ROOT . '/' . $file['filename']), 'dayhour') . '</td>' . "\n";
2603
                        $out .= "</tr>\n";
2604
                    }
2605
                    $out .= '<tr class="liste_total">';
2606
                    $out .= '<td></td>' . "\n";
2607
                    $out .= '<td>' . $langs->trans("Total") . '</td>' . "\n";
2608
                    $out .= '<td align="center"></td>' . "\n";
2609
                    $out .= '<td align="center"></td>' . "\n";
2610
                    $out .= '<td class="right">' . dol_print_size($totalsize) . '</td>' . "\n";
2611
                    $out .= '<td class="right"></td>' . "\n";
2612
                    $out .= "</tr>\n";
2613
                } else {
2614
                    $out .= '<tr class="oddeven"><td colspan="5" class="opacitymedium">' . $langs->trans("None") . '</td></tr>';
2615
                }
2616
                $out .= '</table>';
2617
                $out .= '</div>';
2618
2619
2620
                // Show warning
2621
                if (empty($tmpfilelist) && empty($tmpfilelist2) && empty($tmpfilelist3)) {
2622
                    //setEventMessages($langs->trans("FileIntegrityIsStrictlyConformedWithReference"), null, 'mesgs');
2623
                } else {
2624
                    //setEventMessages($langs->trans("FileIntegritySomeFilesWereRemovedOrModified"), null, 'warnings');
2625
                }
2626
            } else {
2627
                throw new RestException(500, 'Error: Failed to found dolibarr_htdocs_dir into XML file ' . $xmlfile);
2628
            }
2629
2630
2631
            // Scan scripts
2632
            asort($checksumconcat); // Sort list of checksum
2633
            $checksumget = md5(implode(',', $checksumconcat));
2634
            $checksumtoget = trim((string) $xml->dolibarr_htdocs_dir_checksum);
2635
2636
            $outexpectedchecksum = ($checksumtoget ? $checksumtoget : $langs->trans("Unknown"));
2637
            if ($checksumget == $checksumtoget) {
2638
                if (count($file_list['added'])) {
2639
                    $resultcode = 'warning';
2640
                    $resultcomment = 'FileIntegrityIsOkButFilesWereAdded';
2641
                    //$outcurrentchecksum =  $checksumget.' - <span class="'.$resultcode.'">'.$langs->trans("FileIntegrityIsOkButFilesWereAdded").'</span>';
2642
                    $outcurrentchecksum = $checksumget;
2643
                } else {
2644
                    $resultcode = 'ok';
2645
                    $resultcomment = 'Success';
2646
                    //$outcurrentchecksum = '<span class="'.$resultcode.'">'.$checksumget.'</span>';
2647
                    $outcurrentchecksum = $checksumget;
2648
                }
2649
            } else {
2650
                $resultcode = 'error';
2651
                $resultcomment = 'Error';
2652
                //$outcurrentchecksum = '<span class="'.$resultcode.'">'.$checksumget.'</span>';
2653
                $outcurrentchecksum = $checksumget;
2654
            }
2655
        } else {
2656
            throw new RestException(404, 'No signature file known');
2657
        }
2658
2659
        return array('resultcode' => $resultcode, 'resultcomment' => $resultcomment, 'expectedchecksum' => $outexpectedchecksum, 'currentchecksum' => $outcurrentchecksum, 'out' => $out);
2660
    }
2661
2662
2663
    /**
2664
     * Get list of enabled modules
2665
     *
2666
     * @url GET /modules
2667
     *
2668
     * @return  array|mixed Data without useless information
2669
     *
2670
     * @throws RestException 403 Forbidden
2671
     */
2672
    public function getModulesOld()
2673
    {
2674
        global $conf;
2675
2676
        if (
2677
            !DolibarrApiAccess::$user->admin
2678
            && (!getDolGlobalString('API_LOGINS_ALLOWED_FOR_GET_MODULES') || DolibarrApiAccess::$user->login != $conf->global->API_LOGINS_ALLOWED_FOR_GET_MODULES)
2679
        ) {
2680
            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');
2681
        }
2682
2683
        sort($conf->modules);
2684
2685
        return $this->_cleanObjectDatas($conf->modules);
2686
    }
2687
}
2688