Passed
Push — CHECK_API ( 78ffdd )
by Rafael
43:43
created

DolibarrApi::getModuleNamespace()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/* Copyright (C) 2015       Jean-François Ferry         <[email protected]>
4
 * Copyright (C) 2016	    Laurent Destailleur		    <[email protected]>
5
 * Copyright (C) 2020		Frédéric France		        <[email protected]>
6
 * Copyright (C) 2024		MDW							<[email protected]>
7
 * Copyright (C) 2024       Rafael San José             <[email protected]>
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 3 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21
 */
22
23
namespace Dolibarr\Core\Base;
24
25
use Dolibarr\Code\Api\Classes\DolibarrApiAccess;
26
use DoliDB;
27
use Luracast\Restler\Defaults;
28
use Luracast\Restler\Restler;
29
30
/**
31
 * Class for API REST v1
32
 */
33
class DolibarrApi
34
{
35
    /**
36
     * @var Restler $r Restler object
37
     */
38
    public $r;
39
    /**
40
     * @var DoliDB $db Database object
41
     */
42
    protected $db;
43
44
    /**
45
     * Constructor
46
     *
47
     * @param DoliDB $db Database handler
48
     * @param string $cachedir Cache dir
49
     * @param boolean $refreshCache Update cache
50
     */
51
    public function __construct($db, $cachedir = '', $refreshCache = false)
52
    {
53
        global $conf, $dolibarr_main_url_root;
54
55
        if (empty($cachedir)) {
56
            $cachedir = $conf->api->dir_temp;
57
        }
58
        Defaults::$cacheDirectory = $cachedir;
59
60
        $this->db = $db;
61
        $production_mode = (!getDolGlobalString('API_PRODUCTION_MODE') ? false : true);
62
        $this->r = new Restler($production_mode, $refreshCache);
63
64
        $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim($dolibarr_main_url_root));
65
        $urlwithroot = $urlwithouturlroot . DOL_URL_ROOT; // This is to use external domain name found into config file
66
67
        $urlwithouturlrootautodetect = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim(DOL_MAIN_URL_ROOT));
68
        $urlwithrootautodetect = $urlwithouturlroot . DOL_URL_ROOT; // This is to use local domain autodetected by dolibarr from url
69
70
        $this->r->setBaseUrls($urlwithouturlroot, $urlwithouturlrootautodetect);
71
        $this->r->setAPIVersion(1);
72
        //$this->r->setSupportedFormats('json');
73
        //$this->r->setSupportedFormats('jsonFormat');
74
    }
75
76
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
77
78
    /**
79
     * Obtains the namespace of an API module.
80
     *
81
     * @param $moduleName
82
     * @param $className
83
     * @return string
84
     */
85
    public static function getModuleNamespace($moduleName, $className): string
86
    {
87
        return '\\Dolibarr\\Code\\' . $moduleName . '\\Api\\' . $className;
88
    }
89
90
    /**
91
     * Obtains an instance of $moduleName/$className, or null.
92
     *
93
     * @param $moduleName
94
     * @param $className
95
     * @return mixed
96
     */
97
    public static function getModule($moduleName, $className)
98
    {
99
        $namespace = static::getModuleNamespace($moduleName, $className);
100
101
        return new $namespace();
102
    }
103
104
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
105
106
    /**
107
     * Check access by user to a given resource
108
     *
109
     * @param string $resource element to check
110
     * @param int $resource_id Object ID if we want to check a particular record (optional) is linked to a owned thirdparty (optional).
111
     * @param string $dbtablename 'TableName&SharedElement' with Tablename is table where object is stored. SharedElement is an optional key to define where to check entity. Not used if objectid is null (optional)
112
     * @param string $feature2 Feature to check, second level of permission (optional). Can be or check with 'level1|level2'.
113
     * @param string $dbt_keyfield Field name for socid foreign key if not fk_soc. Not used if objectid is null (optional)
114
     * @param string $dbt_select Field name for select if not rowid. Not used if objectid is null (optional)
115
     * @return bool
116
     */
117
    protected static function _checkAccessToResource($resource, $resource_id = 0, $dbtablename = '', $feature2 = '', $dbt_keyfield = 'fk_soc', $dbt_select = 'rowid')
118
    {
119
        // phpcs:enable
120
        // Features/modules to check
121
        $featuresarray = array($resource);
122
        if (preg_match('/&/', $resource)) {
123
            $featuresarray = explode("&", $resource);
124
        } elseif (preg_match('/\|/', $resource)) {
125
            $featuresarray = explode("|", $resource);
126
        }
127
128
        // More subfeatures to check
129
        if (!empty($feature2)) {
130
            $feature2 = explode("|", $feature2);
131
        }
132
133
        return checkUserAccessToObject(DolibarrApiAccess::$user, $featuresarray, $resource_id, $dbtablename, $feature2, $dbt_keyfield, $dbt_select);
134
    }
135
136
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
137
138
    /**
139
     * Function to forge a SQL criteria from a Generic filter string.
140
     * Function no more used. Kept for backward compatibility with old APIs of modules
141
     *
142
     * @param array $matches Array of found string by regex search.
143
     *                              Each entry is 1 and only 1 criteria.
144
     *                              Example: "t.ref:like:'SO-%'", "t.date_creation:<:'20160101'", "t.date_creation:<:'2016-01-01 12:30:00'", "t.nature:is:NULL", "t.field2:isnot:NULL"
145
     * @return string               Forged criteria. Example: "t.field like 'abc%'"
146
     */
147
    protected static function _forge_criteria_callback($matches)
148
    {
149
        return dolForgeCriteriaCallback($matches);
150
    }
151
152
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
153
154
    /**
155
     * Check and convert a string depending on its type/name.
156
     *
157
     * @param string $field Field name
158
     * @param string|array $value Value to check/clean
159
     * @param Object $object Object
160
     * @return  string|array                Value cleaned
161
     */
162
    protected function _checkValForAPI($field, $value, $object)
163
    {
164
        // phpcs:enable
165
        if (!is_array($value)) {
166
            // Sanitize the value using its type declared into ->fields of $object
167
            if (!empty($object->fields) && !empty($object->fields[$field]) && !empty($object->fields[$field]['type'])) {
168
                if (strpos($object->fields[$field]['type'], 'int') || strpos($object->fields[$field]['type'], 'double') || in_array($object->fields[$field]['type'], array('real', 'price', 'stock'))) {
169
                    return sanitizeVal($value, 'int');
170
                }
171
                if ($object->fields[$field]['type'] == 'html') {
172
                    return sanitizeVal($value, 'restricthtml');
173
                }
174
                if ($object->fields[$field]['type'] == 'select') {
175
                    // Check values are in the list of possible 'options'
176
                    // TODO
177
                }
178
                if ($object->fields[$field]['type'] == 'sellist' || $object->fields[$field]['type'] == 'checkbox') {
179
                    // TODO
180
                }
181
                if ($object->fields[$field]['type'] == 'boolean' || $object->fields[$field]['type'] == 'radio') {
182
                    // TODO
183
                }
184
                if ($object->fields[$field]['type'] == 'email') {
185
                    return sanitizeVal($value, 'email');
186
                }
187
                if ($object->fields[$field]['type'] == 'password') {
188
                    return sanitizeVal($value, 'none');
189
                }
190
                // Others will use 'alphanohtml'
191
            }
192
193
            if (in_array($field, array('note', 'note_private', 'note_public', 'desc', 'description'))) {
194
                return sanitizeVal($value, 'restricthtml');
195
            } else {
196
                return sanitizeVal($value, 'alphanohtml');
197
            }
198
        } else {    // Example when $field = 'extrafields' and $value = content of $object->array_options
199
            $newarrayvalue = array();
200
            foreach ($value as $tmpkey => $tmpvalue) {
201
                $newarrayvalue[$tmpkey] = $this->_checkValForAPI($tmpkey, $tmpvalue, $object);
202
            }
203
204
            return $newarrayvalue;
205
        }
206
    }
207
208
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
209
210
    /**
211
     * Filter properties that will be returned on object
212
     *
213
     * @param Object $object Object to clean
214
     * @param String $properties Comma separated list of properties names
215
     * @return  Object                  Object with cleaned properties
216
     */
217
    protected function _filterObjectProperties($object, $properties)
218
    {
219
        // phpcs:enable
220
        // If properties is empty, we return all properties
221
        if (empty($properties)) {
222
            return $object;
223
        }
224
225
        // Copy of exploded array for efficiency
226
        $arr_properties = explode(',', $properties);
227
        $magic_properties = array();
228
        $real_properties = get_object_vars($object);
229
230
        // Unsetting real properties may unset magic properties.
231
        // We keep a copy of the requested magic properties
232
        foreach ($arr_properties as $key) {
233
            if (!array_key_exists($key, $real_properties)) {
234
                // Not a real property,
235
                // check if $key is a magic property (we want to keep '$obj->$key')
236
                if (property_exists($object, $key) && isset($object->$key)) {
237
                    $magic_properties[$key] = $object->$key;
238
                }
239
            }
240
        }
241
242
        // Filter real properties (may indirectly unset magic properties)
243
        foreach (get_object_vars($object) as $key => $value) {
244
            if (!in_array($key, $arr_properties)) {
245
                unset($object->$key);
246
            }
247
        }
248
249
        // Restore the magic properties
250
        foreach ($magic_properties as $key => $value) {
251
            $object->$key = $value;
252
        }
253
254
        return $object;
255
    }
256
257
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
258
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
259
260
    /**
261
     * Clean sensible object datas
262
     *
263
     * @param Object $object Object to clean
264
     * @return  Object              Object with cleaned properties
265
     */
266
    protected function _cleanObjectDatas($object)
267
    {
268
        // phpcs:enable
269
        // Remove $db object property for object
270
        unset($object->db);
271
        unset($object->isextrafieldmanaged);
272
        unset($object->ismultientitymanaged);
273
        unset($object->restrictiononfksoc);
274
        unset($object->table_rowid);
275
        unset($object->pass);
276
        unset($object->pass_indatabase);
277
278
        // Remove linkedObjects. We should already have and keep only linkedObjectsIds that avoid huge responses
279
        unset($object->linkedObjects);
280
        //unset($object->lines[$i]->linked_objects);        // This is the array to create linked object during create
281
282
        unset($object->fields);
283
        unset($object->oldline);
284
285
        unset($object->error);
286
        unset($object->errors);
287
        unset($object->errorhidden);
288
289
        unset($object->ref_previous);
290
        unset($object->ref_next);
291
        unset($object->imgWidth);
292
        unset($object->imgHeight);
293
        unset($object->barcode_type_code);
294
        unset($object->barcode_type_label);
295
296
        unset($object->mode_reglement);     // We use mode_reglement_id now
297
        unset($object->cond_reglement);     // We use cond_reglement_id now
298
        unset($object->note);               // We use note_public or note_private now
299
        unset($object->contact);            // We use contact_id now
300
        unset($object->thirdparty);         // We use thirdparty_id or fk_soc or socid now
301
302
        unset($object->projet); // Should be fk_project
303
        unset($object->project); // Should be fk_project
304
        unset($object->fk_projet); // Should be fk_project
305
        unset($object->author); // Should be fk_user_author
306
        unset($object->timespent_old_duration);
307
        unset($object->timespent_id);
308
        unset($object->timespent_duration);
309
        unset($object->timespent_date);
310
        unset($object->timespent_datehour);
311
        unset($object->timespent_withhour);
312
        unset($object->timespent_fk_user);
313
        unset($object->timespent_note);
314
        unset($object->fk_delivery_address);
315
        unset($object->model_pdf);
316
        unset($object->sendtoid);
317
        unset($object->name_bis);
318
        unset($object->newref);
319
        unset($object->oldref);
320
        unset($object->alreadypaid);
321
        unset($object->openid);
322
        unset($object->fk_bank);
323
        unset($object->showphoto_on_popup);
324
        unset($object->nb);
325
        unset($object->nbphoto);
326
        unset($object->output);
327
        unset($object->tpl);
328
        //unset($object->libelle);
329
330
        unset($object->stats_propale);
331
        unset($object->stats_commande);
332
        unset($object->stats_contrat);
333
        unset($object->stats_facture);
334
        unset($object->stats_commande_fournisseur);
335
        unset($object->stats_reception);
336
        unset($object->stats_mrptoconsume);
337
        unset($object->stats_mrptoproduce);
338
339
        unset($object->origin_object);
340
        unset($object->origin);
341
        unset($object->element);
342
        unset($object->element_for_permission);
343
        unset($object->fk_element);
344
        unset($object->table_element);
345
        unset($object->table_element_line);
346
        unset($object->class_element_line);
347
        unset($object->picto);
348
        unset($object->linked_objects);
349
350
        unset($object->fieldsforcombobox);
351
        unset($object->regeximgext);
352
353
        unset($object->skip_update_total);
354
        unset($object->context);
355
        unset($object->next_prev_filter);
356
357
        unset($object->region);
358
        unset($object->region_code);
359
        unset($object->country);
360
        unset($object->state);
361
        unset($object->state_code);
362
        unset($object->fk_departement);
363
        unset($object->departement);
364
        unset($object->departement_code);
365
366
        unset($object->libelle_statut);
367
        unset($object->libelle_paiement);
368
        unset($object->labelStatus);
369
        unset($object->labelStatusShort);
370
371
        unset($object->actionmsg);
372
        unset($object->actionmsg2);
373
374
        unset($object->prefix_comm);
375
376
        if (!isset($object->table_element) || $object->table_element != 'ticket') {
377
            unset($object->comments);
378
        }
379
380
        // Remove the $oldcopy property because it is not supported by the JSON
381
        // encoder. The following error is generated when trying to serialize
382
        // it: "Error encoding/decoding JSON: Type is not supported"
383
        // Note: Event if this property was correctly handled by the JSON
384
        // encoder, it should be ignored because keeping it would let the API
385
        // have a very strange behavior: calling PUT and then GET on the same
386
        // resource would give different results:
387
        // PUT /objects/{id} -> returns object with oldcopy = previous version of the object
388
        // GET /objects/{id} -> returns object with oldcopy empty
389
        unset($object->oldcopy);
390
391
        // If object has lines, remove $db property
392
        if (isset($object->lines) && is_array($object->lines) && count($object->lines) > 0) {
393
            $nboflines = count($object->lines);
394
            for ($i = 0; $i < $nboflines; $i++) {
395
                $this->_cleanObjectDatas($object->lines[$i]);
396
397
                unset($object->lines[$i]->contact);
398
                unset($object->lines[$i]->contact_id);
399
                unset($object->lines[$i]->country);
400
                unset($object->lines[$i]->country_id);
401
                unset($object->lines[$i]->country_code);
402
                unset($object->lines[$i]->mode_reglement_id);
403
                unset($object->lines[$i]->mode_reglement_code);
404
                unset($object->lines[$i]->mode_reglement);
405
                unset($object->lines[$i]->cond_reglement_id);
406
                unset($object->lines[$i]->cond_reglement_code);
407
                unset($object->lines[$i]->cond_reglement);
408
                unset($object->lines[$i]->fk_delivery_address);
409
                unset($object->lines[$i]->fk_projet);
410
                unset($object->lines[$i]->fk_project);
411
                unset($object->lines[$i]->thirdparty);
412
                unset($object->lines[$i]->user);
413
                unset($object->lines[$i]->model_pdf);
414
                unset($object->lines[$i]->note_public);
415
                unset($object->lines[$i]->note_private);
416
                unset($object->lines[$i]->fk_incoterms);
417
                unset($object->lines[$i]->label_incoterms);
418
                unset($object->lines[$i]->location_incoterms);
419
                unset($object->lines[$i]->name);
420
                unset($object->lines[$i]->lastname);
421
                unset($object->lines[$i]->firstname);
422
                unset($object->lines[$i]->civility_id);
423
                unset($object->lines[$i]->fk_multicurrency);
424
                unset($object->lines[$i]->multicurrency_code);
425
                unset($object->lines[$i]->shipping_method_id);
426
            }
427
        }
428
429
        if (!empty($object->thirdparty) && is_object($object->thirdparty)) {
430
            $this->_cleanObjectDatas($object->thirdparty);
431
        }
432
433
        if (!empty($object->product) && is_object($object->product)) {
434
            $this->_cleanObjectDatas($object->product);
435
        }
436
437
        return $object;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $object also could return the type object which is incompatible with the documented return type object.
Loading history...
438
    }
439
440
    /**
441
     * Return if a $sqlfilters parameter is valid
442
     * Function no more used. Kept for backward compatibility with old APIs of modules
443
     *
444
     * @param string $sqlfilters sqlfilter string
445
     * @param string $error Error message
446
     * @return  boolean|string                  True if valid, False if not valid
447
     */
448
    protected function _checkFilters($sqlfilters, &$error = '')
449
    {
450
        // phpcs:enable
451
        $firstandlastparenthesis = 0;
452
        return dolCheckFilters($sqlfilters, $error, $firstandlastparenthesis);
453
    }
454
}
455