Setup::getListOfCivilities()   B
last analyzed

Complexity

Conditions 9
Paths 38

Size

Total Lines 46
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 28
nc 38
nop 8
dl 0
loc 46
rs 8.0555
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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