Passed
Push — EXTRACT_CLASSES ( ae6b5c...83d77a )
by Rafael
60:14 queued 23:58
created

Contacts   F

Complexity

Total Complexity 97

Size/Duplication

Total Lines 567
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 227
dl 0
loc 567
rs 2
c 0
b 0
f 0
wmc 97

13 Methods

Rating   Name   Duplication   Size   Complexity  
A _validate() 0 11 3
B get() 0 33 8
C put() 0 42 14
F index() 0 95 24
A delete() 0 15 4
B getByEmail() 0 33 8
B createUser() 0 42 8
A __construct() 0 9 1
A _cleanObjectDatas() 0 16 1
A deleteCategory() 0 26 6
B post() 0 30 11
A addCategory() 0 26 6
A getCategories() 0 15 3

How to fix   Complexity   

Complex Class

Complex classes like Contacts often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Contacts, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/* Copyright (C) 2015       Jean-François Ferry         <[email protected]>
4
 * Copyright (C) 2019       Frédéric France             <[email protected]>
5
 * Copyright (C) 2024		MDW							<[email protected]>
6
 * Copyright (C) 2024       Rafael San José             <[email protected]>
7
 *
8
 * This program is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation; either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20
 */
21
22
namespace Dolibarr\Code\Societe\Api;
23
24
use Dolibarr\Core\Base\DolibarrApi;
25
use Luracast\Restler\RestException;
26
27
//require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
28
//require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
29
30
31
/**
32
 * API class for contacts
33
 *
34
 * @access protected
35
 * @class  DolibarrApiAccess {@requires user,external}
36
 */
37
class Contacts extends DolibarrApi
38
{
39
    /**
40
     *
41
     * @var array   $FIELDS     Mandatory fields, checked when create and update object
42
     */
43
    public static $FIELDS = array(
44
        'lastname',
45
    );
46
47
    /**
48
     * @var Contact $contact {@type Contact}
49
     */
50
    public $contact;
51
52
    /**
53
     * Constructor
54
     */
55
    public function __construct()
56
    {
57
        global $db, $conf;
58
        $this->db = $db;
59
60
        require_once constant('DOL_DOCUMENT_ROOT') . '/contact/class/contact.class.php';
61
        require_once constant('DOL_DOCUMENT_ROOT') . '/categories/class/categorie.class.php';
62
63
        $this->contact = new Contact($this->db);
64
    }
65
66
    /**
67
     * Get properties of a contact object
68
     *
69
     * Return an array with contact information
70
     *
71
     * @param   int    $id                  ID of contact
72
     * @param   int    $includecount        Count and return also number of elements the contact is used as a link for
73
     * @param   int    $includeroles        Includes roles of the contact
74
     * @return  object                      data without useless information
75
     *
76
     * @throws  RestException
77
     */
78
    public function get($id, $includecount = 0, $includeroles = 0)
79
    {
80
        if (!DolibarrApiAccess::$user->hasRight('societe', 'contact', 'lire')) {
81
            throw new RestException(403, 'No permission to read contacts');
82
        }
83
84
        if ($id === 0) {
85
            $result = $this->contact->initAsSpecimen();
86
        } else {
87
            $result = $this->contact->fetch($id);
88
        }
89
90
        if (!$result) {
91
            throw new RestException(404, 'Contact not found');
92
        }
93
94
        if (!DolibarrApi::_checkAccessToResource('contact', $this->contact->id, 'socpeople&societe')) {
95
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
96
        }
97
98
        if ($includecount) {
99
            $this->contact->load_ref_elements();
100
        }
101
102
        if ($includeroles) {
103
            $this->contact->fetchRoles();
104
        }
105
106
        if (isModEnabled('mailing')) {
107
            $this->contact->getNoEmail();
108
        }
109
110
        return $this->_cleanObjectDatas($this->contact);
111
    }
112
113
    /**
114
     * Get properties of a contact object by Email
115
     *
116
     * @param   string  $email                  Email of contact
117
     * @param   int    $includecount        Count and return also number of elements the contact is used as a link for
118
     * @param   int    $includeroles        Includes roles of the contact
119
     * @return  array|mixed data without useless information
120
     *
121
     * @url GET email/{email}
122
     *
123
     * @throws RestException 401     Insufficient rights
124
     * @throws RestException 404     User or group not found
125
     */
126
    public function getByEmail($email, $includecount = 0, $includeroles = 0)
127
    {
128
        if (!DolibarrApiAccess::$user->hasRight('societe', 'contact', 'lire')) {
129
            throw new RestException(403, 'No permission to read contacts');
130
        }
131
132
        if (empty($email)) {
133
            $result = $this->contact->initAsSpecimen();
134
        } else {
135
            $result = $this->contact->fetch('', '', '', $email);
136
        }
137
138
        if (!$result) {
139
            throw new RestException(404, 'Contact not found');
140
        }
141
142
        if (!DolibarrApi::_checkAccessToResource('contact', $this->contact->id, 'socpeople&societe')) {
143
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
144
        }
145
146
        if ($includecount) {
147
            $this->contact->load_ref_elements();
148
        }
149
150
        if ($includeroles) {
151
            $this->contact->fetchRoles();
152
        }
153
154
        if (isModEnabled('mailing')) {
155
            $this->contact->getNoEmail();
156
        }
157
158
        return $this->_cleanObjectDatas($this->contact);
159
    }
160
161
    /**
162
     * List contacts
163
     *
164
     * Get a list of contacts
165
     *
166
     * @param string    $sortfield          Sort field
167
     * @param string    $sortorder          Sort order
168
     * @param int       $limit              Limit for list
169
     * @param int       $page               Page number
170
     * @param string    $thirdparty_ids     Thirdparty ids to filter contacts of (example '1' or '1,2,3') {@pattern /^[0-9,]*$/i}
171
     * @param int       $category   Use this param to filter list by category
172
     * @param string    $sqlfilters         Other criteria to filter answers separated by a comma. Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101')"
173
     * @param int       $includecount       Count and return also number of elements the contact is used as a link for
174
     * @param int       $includeroles        Includes roles of the contact
175
     * @param string    $properties Restrict the data returned to these properties. Ignored if empty. Comma separated list of properties names
176
     * @return Contact[]                        Array of contact objects
177
     *
178
     * @throws RestException
179
     */
180
    public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $thirdparty_ids = '', $category = 0, $sqlfilters = '', $includecount = 0, $includeroles = 0, $properties = '')
181
    {
182
        global $db, $conf;
183
184
        $obj_ret = array();
185
186
        if (!DolibarrApiAccess::$user->hasRight('societe', 'contact', 'lire')) {
187
            throw new RestException(403, 'No permission to read contacts');
188
        }
189
190
        // case of external user, $thirdparty_ids param is ignored and replaced by user's socid
191
        $socids = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : $thirdparty_ids;
192
193
        // If the internal user must only see his customers, force searching by him
194
        $search_sale = 0;
195
        if (!DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir') && !$socids) {
196
            $search_sale = DolibarrApiAccess::$user->id;
197
        }
198
199
        $sql = "SELECT t.rowid";
200
        $sql .= " FROM " . MAIN_DB_PREFIX . "socpeople as t";
201
        if ($category > 0) {
202
            $sql .= ", " . MAIN_DB_PREFIX . "categorie_contact as c";
203
        }
204
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "socpeople_extrafields as te ON te.fk_object = t.rowid";
205
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON t.fk_soc = s.rowid";
206
        $sql .= ' WHERE t.entity IN (' . getEntity('contact') . ')';
207
        if ($socids) {
208
            $sql .= " AND t.fk_soc IN (" . $this->db->sanitize($socids) . ")";
209
        }
210
        // Search on sale representative
211
        if ($search_sale && $search_sale != '-1') {
212
            if ($search_sale == -2) {
213
                $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = t.fk_soc)";
214
            } elseif ($search_sale > 0) {
215
                $sql .= " AND EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = t.fk_soc AND sc.fk_user = " . ((int) $search_sale) . ")";
216
            }
217
        }
218
        // Select contacts of given category
219
        if ($category > 0) {
220
            $sql .= " AND c.fk_categorie = " . ((int) $category);
221
            $sql .= " AND c.fk_socpeople = t.rowid ";
222
        }
223
224
        // Add sql filters
225
        if ($sqlfilters) {
226
            $errormessage = '';
227
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
228
            if ($errormessage) {
229
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
230
            }
231
        }
232
233
        $sql .= $this->db->order($sortfield, $sortorder);
234
235
        if ($limit) {
236
            if ($page < 0) {
237
                $page = 0;
238
            }
239
            $offset = $limit * $page;
240
241
            $sql .= $this->db->plimit($limit + 1, $offset);
242
        }
243
        $result = $this->db->query($sql);
244
        if ($result) {
245
            $num = $this->db->num_rows($result);
246
            $min = min($num, ($limit <= 0 ? $num : $limit));
247
            $i = 0;
248
            while ($i < $min) {
249
                $obj = $this->db->fetch_object($result);
250
                $contact_static = new Contact($this->db);
251
                if ($contact_static->fetch($obj->rowid)) {
252
                    $contact_static->fetchRoles();
253
                    if ($includecount) {
254
                        $contact_static->load_ref_elements();
255
                    }
256
                    if ($includeroles) {
257
                        $contact_static->fetchRoles();
258
                    }
259
                    if (isModEnabled('mailing')) {
260
                        $contact_static->getNoEmail();
261
                    }
262
263
                    $obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($contact_static), $properties);
264
                }
265
266
                $i++;
267
            }
268
        } else {
269
            throw new RestException(503, 'Error when retrieve contacts : ' . $sql);
270
        }
271
        if (!count($obj_ret)) {
272
            throw new RestException(404, 'Contacts not found');
273
        }
274
        return $obj_ret;
275
    }
276
277
    /**
278
     * Create contact object
279
     *
280
     * @param   array   $request_data   Request datas
281
     * @return  int     ID of contact
282
     *
283
     * @suppress PhanPluginUnknownArrayMethodParamType  Luracast limitation
284
     */
285
    public function post($request_data = null)
286
    {
287
        if (!DolibarrApiAccess::$user->hasRight('societe', 'contact', 'creer')) {
288
            throw new RestException(403, 'No permission to create/update contacts');
289
        }
290
        // Check mandatory fields
291
        $result = $this->_validate($request_data);
292
293
        foreach ($request_data as $field => $value) {
294
            if ($field === 'caller') {
295
                // Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller
296
                $this->contact->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
297
                continue;
298
            }
299
            if ($field == 'array_options' && is_array($value)) {
300
                foreach ($value as $index => $val) {
301
                    $this->contact->array_options[$index] = $this->_checkValForAPI('extrafields', $val, $this->contact);
302
                }
303
                continue;
304
            }
305
306
            $this->contact->$field = $this->_checkValForAPI($field, $value, $this->contact);
307
        }
308
        if ($this->contact->create(DolibarrApiAccess::$user) < 0) {
309
            throw new RestException(500, "Error creating contact", array_merge(array($this->contact->error), $this->contact->errors));
310
        }
311
        if (isModEnabled('mailing') && !empty($this->contact->email) && isset($this->contact->no_email)) {
312
            $this->contact->setNoEmail($this->contact->no_email);
313
        }
314
        return $this->contact->id;
315
    }
316
317
    /**
318
     * Update contact
319
     *
320
     * @param   int     $id                 Id of contact to update
321
     * @param   array   $request_data       Datas
322
     * @return  Object|false                Updated object, false when issue toupdate
323
     *
324
     * @throws RestException 401
325
     * @throws RestException 404
326
     * @throws RestException 500
327
     */
328
    public function put($id, $request_data = null)
329
    {
330
        if (!DolibarrApiAccess::$user->hasRight('societe', 'contact', 'creer')) {
331
            throw new RestException(403, 'No permission to create/update contacts');
332
        }
333
334
        $result = $this->contact->fetch($id);
335
        if (!$result) {
336
            throw new RestException(404, 'Contact not found');
337
        }
338
339
        if (!DolibarrApi::_checkAccessToResource('contact', $this->contact->id, 'socpeople&societe')) {
340
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
341
        }
342
343
        foreach ($request_data as $field => $value) {
344
            if ($field == 'id') {
345
                continue;
346
            }
347
            if ($field === 'caller') {
348
                // Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller
349
                $this->contact->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
350
                continue;
351
            }
352
            if ($field == 'array_options' && is_array($value)) {
353
                foreach ($value as $index => $val) {
354
                    $this->contact->array_options[$index] = $this->_checkValForAPI('extrafields', $val, $this->contact);
355
                }
356
                continue;
357
            }
358
359
            $this->contact->$field = $this->_checkValForAPI($field, $value, $this->contact);
360
        }
361
362
        if (isModEnabled('mailing') && !empty($this->contact->email) && isset($this->contact->no_email)) {
363
            $this->contact->setNoEmail($this->contact->no_email);
364
        }
365
366
        if ($this->contact->update($id, DolibarrApiAccess::$user, 0, 'update') > 0) {
367
            return $this->get($id);
368
        } else {
369
            throw new RestException(500, $this->contact->error);
370
        }
371
    }
372
373
    /**
374
     * Delete contact
375
     *
376
     * @param   int     $id Contact ID
377
     * @return  integer
378
     */
379
    public function delete($id)
380
    {
381
        if (!DolibarrApiAccess::$user->hasRight('societe', 'contact', 'supprimer')) {
382
            throw new RestException(403, 'No permission to delete contacts');
383
        }
384
        $result = $this->contact->fetch($id);
385
        if (!$result) {
386
            throw new RestException(404, 'Contact not found');
387
        }
388
389
        if (!DolibarrApi::_checkAccessToResource('contact', $this->contact->id, 'socpeople&societe')) {
390
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
391
        }
392
        $this->contact->oldcopy = clone $this->contact;
393
        return $this->contact->delete(DolibarrApiAccess::$user);
394
    }
395
396
    /**
397
     * Create an user account object from contact (external user)
398
     *
399
     * @param   int     $id   Id of contact
400
     * @param   array   $request_data   Request datas
401
     * @return  int     ID of user
402
     *
403
     * @url POST {id}/createUser
404
     * @suppress PhanPluginUnknownArrayMethodParamType  Luracast limitation
405
     */
406
    public function createUser($id, $request_data = null)
407
    {
408
        //if (!DolibarrApiAccess::$user->hasRight('user', 'user', 'creer')) {
409
        //throw new RestException(403);
410
        //}
411
412
        if (!isset($request_data["login"])) {
413
            throw new RestException(400, "login field missing");
414
        }
415
        if (!isset($request_data["password"])) {
416
            throw new RestException(400, "password field missing");
417
        }
418
419
        if (!DolibarrApiAccess::$user->hasRight('societe', 'contact', 'lire')) {
420
            throw new RestException(403, 'No permission to read contacts');
421
        }
422
        if (!DolibarrApiAccess::$user->hasRight('user', 'user', 'creer')) {
423
            throw new RestException(403, 'No permission to create user');
424
        }
425
426
        $contact = new Contact($this->db);
427
        $contact->fetch($id);
428
        if ($contact->id <= 0) {
429
            throw new RestException(404, 'Contact not found');
430
        }
431
432
        if (!DolibarrApi::_checkAccessToResource('contact', $contact->id, 'socpeople&societe')) {
433
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
434
        }
435
436
        // Check mandatory fields
437
        $login = $request_data["login"];
438
        $password = $request_data["password"];
439
        $useraccount = new User($this->db);
440
        $result = $useraccount->create_from_contact($contact, $login, $password);
441
        if ($result <= 0) {
442
            throw new RestException(500, "User not created");
443
        }
444
        // password parameter not used in create_from_contact
445
        $useraccount->setPassword($useraccount, $password);
446
447
        return $result;
448
    }
449
450
    /**
451
     * Get categories for a contact
452
     *
453
     * @param int       $id         ID of contact
454
     * @param string    $sortfield  Sort field
455
     * @param string    $sortorder  Sort order
456
     * @param int       $limit      Limit for list
457
     * @param int       $page       Page number
458
     *
459
     * @return mixed
460
     *
461
     * @url GET {id}/categories
462
     */
463
    public function getCategories($id, $sortfield = "s.rowid", $sortorder = 'ASC', $limit = 0, $page = 0)
464
    {
465
        if (!DolibarrApiAccess::$user->hasRight('categorie', 'lire')) {
466
            throw new RestException(403);
467
        }
468
469
        $categories = new Categorie($this->db);
470
471
        $result = $categories->getListForItem($id, 'contact', $sortfield, $sortorder, $limit, $page);
472
473
        if ($result < 0) {
474
            throw new RestException(503, 'Error when retrieve category list : ' . $categories->error);
475
        }
476
477
        return $result;
478
    }
479
480
    /**
481
     * Add a category to a contact
482
     *
483
     * @url PUT {id}/categories/{category_id}
484
     *
485
     * @param   int     $id             Id of contact
486
     * @param   int     $category_id    Id of category
487
     *
488
     * @return  mixed
489
     *
490
     * @throws RestException 401 Insufficient rights
491
     * @throws RestException 404 Category or contact not found
492
     */
493
    public function addCategory($id, $category_id)
494
    {
495
        if (!DolibarrApiAccess::$user->hasRight('societe', 'contact', 'creer')) {
496
            throw new RestException(403, 'Insufficient rights');
497
        }
498
499
        $result = $this->contact->fetch($id);
500
        if (!$result) {
501
            throw new RestException(404, 'Contact not found');
502
        }
503
        $category = new Categorie($this->db);
504
        $result = $category->fetch($category_id);
505
        if (!$result) {
506
            throw new RestException(404, 'category not found');
507
        }
508
509
        if (!DolibarrApi::_checkAccessToResource('contact', $this->contact->id)) {
510
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
511
        }
512
        if (!DolibarrApi::_checkAccessToResource('category', $category->id)) {
513
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
514
        }
515
516
        $category->add_type($this->contact, 'contact');
517
518
        return $this->_cleanObjectDatas($this->contact);
519
    }
520
521
    /**
522
     * Remove the link between a category and a contact
523
     *
524
     * @url DELETE {id}/categories/{category_id}
525
     *
526
     * @param   int     $id             Id of contact
527
     * @param   int     $category_id    Id of category
528
     * @return  mixed
529
     *
530
     * @throws  RestException 401     Insufficient rights
531
     * @throws  RestException 404     Category or contact not found
532
     */
533
    public function deleteCategory($id, $category_id)
534
    {
535
        if (!DolibarrApiAccess::$user->hasRight('societe', 'contact', 'creer')) {
536
            throw new RestException(403, 'Insufficient rights');
537
        }
538
539
        $result = $this->contact->fetch($id);
540
        if (!$result) {
541
            throw new RestException(404, 'Contact not found');
542
        }
543
        $category = new Categorie($this->db);
544
        $result = $category->fetch($category_id);
545
        if (!$result) {
546
            throw new RestException(404, 'category not found');
547
        }
548
549
        if (!DolibarrApi::_checkAccessToResource('contact', $this->contact->id)) {
550
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
551
        }
552
        if (!DolibarrApi::_checkAccessToResource('category', $category->id)) {
553
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
554
        }
555
556
        $category->del_type($this->contact, 'contact');
557
558
        return $this->_cleanObjectDatas($this->contact);
559
    }
560
561
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
562
    /**
563
     * Clean sensible object datas
564
     *
565
     * @param   Object  $object     Object to clean
566
     * @return  Object              Object with cleaned properties
567
     */
568
    protected function _cleanObjectDatas($object)
569
    {
570
		// phpcs:enable
571
        $object = parent::_cleanObjectDatas($object);
572
573
        unset($object->total_ht);
574
        unset($object->total_tva);
575
        unset($object->total_localtax1);
576
        unset($object->total_localtax2);
577
        unset($object->total_ttc);
578
579
        unset($object->note);
580
        unset($object->lines);
581
        unset($object->thirdparty);
582
583
        return $object;
584
    }
585
586
    /**
587
     * Validate fields before create or update object
588
     *
589
     * @param   string[]|null     $data   Data to validate
590
     * @return  string[]
591
     * @throws  RestException
592
     */
593
    private function _validate($data)
594
    {
595
        $contact = array();
596
        foreach (Contacts::$FIELDS as $field) {
597
            if (!isset($data[$field])) {
598
                throw new RestException(400, "$field field missing");
599
            }
600
            $contact[$field] = $data[$field];
601
        }
602
603
        return $contact;
604
    }
605
}
606