Passed
Push — dev ( f7d146...05f415 )
by Rafael
60:50
created

vCard::buildVCardString()   F

Complexity

Conditions 43
Paths > 20000

Size

Total Lines 154
Code Lines 65

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 43
eloc 65
nc 456192
nop 4
dl 0
loc 154
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/* Copyright (C)            Kai Blankenhorn             <[email protected]>
4
 * Copyright (C) 2005-2017  Laurent Destailleur         <[email protected]>
5
 * Copyright (C) 2020		Tobias Sekan		        <[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\Core\Classes;
23
24
/**
25
 *  \file       htdocs/core/class/vcard.class.php
26
 *  \brief      Class to manage vCard files
27
 */
28
29
30
/**
31
 * Encode a string for vCard
32
 *
33
 * @param   string  $string     String to encode
34
 * @return  string              String encoded
35
 */
36
function encode($string)
37
{
38
    return str_replace(";", "\;", (dol_quoted_printable_encode($string)));
39
}
40
41
42
/**
43
 * Taken from php documentation comments
44
 * No more used
45
 *
46
 * @param   string  $input      String
47
 * @param   int     $line_max   Max length of lines
48
 * @return  string              Encoded string
49
 */
50
function dol_quoted_printable_encode($input, $line_max = 76)
51
{
52
    $hex = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
53
    $lines = preg_split("/(\?:\r\n|\r|\n)/", $input);
54
    $eol = "\r\n";
55
    $linebreak = "=0D=0A";
56
    $escape = "=";
57
    $output = "";
58
59
    $num = count($lines);
60
    for ($j = 0; $j < $num; $j++) {
61
        $line = $lines[$j];
62
        $linlen = strlen($line);
63
        $newline = "";
64
        for ($i = 0; $i < $linlen; $i++) {
65
            $c = substr($line, $i, 1);
66
            $dec = ord($c);
67
            if (($dec == 32) && ($i == ($linlen - 1))) { // convert space at eol only
68
                $c = "=20";
69
            } elseif (($dec == 61) || ($dec < 32) || ($dec > 126)) { // always encode "\t", which is *not* required
70
                $h2 = floor($dec / 16);
71
                $h1 = floor($dec % 16);
72
                $c = $escape . $hex["$h2"] . $hex["$h1"];
73
            }
74
            if ((strlen($newline) + strlen($c)) >= $line_max) { // CRLF is not counted
75
                $output .= $newline . $escape . $eol; // soft line break; " =\r\n" is okay
76
                $newline = "    ";
77
            }
78
            $newline .= $c;
79
        } // end of for
80
        $output .= $newline;
81
        if ($j < count($lines) - 1) {
82
            $output .= $linebreak;
83
        }
84
    }
85
    return trim($output);
86
}
87
88
89
/**
90
 *  Class to build vCard files
91
 */
92
class vCard
93
{
94
    /**
95
     * @var array array of properties
96
     */
97
    public $properties;
98
99
    /**
100
     * @var string filename
101
     */
102
    public $filename;
103
104
    /**
105
     * @var string encoding
106
     */
107
    public $encoding = "ENCODING=QUOTED-PRINTABLE";
108
109
110
    /**
111
     *  mise en forme du numero de telephone
112
     *
113
     *  @param  int     $number     numero de telephone
114
     *  @param  string  $type       Type ('cell')
115
     *  @return void
116
     */
117
    public function setPhoneNumber($number, $type = "")
118
    {
119
        // type may be PREF | WORK | HOME | VOICE | FAX | MSG | CELL | PAGER | BBS | CAR | MODEM | ISDN | VIDEO or any senseful combination, e.g. "PREF;WORK;VOICE"
120
        $key = "TEL";
121
        if ($type != "") {
122
            $key .= ";" . $type;
123
        }
124
        $key .= ";VALUE=uri";
125
        //$key .= ";".$this->encoding;
126
        $this->properties[$key] = 'tel:' . $number;
127
    }
128
129
    /**
130
     *  mise en forme de la photo
131
     *  warning NON TESTE !
132
     *
133
     *  @param  string  $type           Type 'image/jpeg' or 'JPEG'
134
     *  @param  string  $photo          Photo
135
     *  @return void
136
     */
137
    public function setPhoto($type, $photo)
138
    {
139
        // $type = "GIF" | "JPEG"
140
        //$this->properties["PHOTO;MEDIATYPE=$type;ENCODING=BASE64"] = base64_encode($photo);
141
        $this->properties["PHOTO;MEDIATYPE=$type"] = $photo;        // must be url of photo
142
        //$this->properties["PHOTO;TYPE=$type;ENCODING=BASE64"] = base64_encode($photo);   // must be content of image
143
    }
144
145
    /**
146
     *  mise en forme du nom format
147
     *
148
     *  @param  string  $name           Name
149
     *  @return void
150
     */
151
    public function setFormattedName($name)
152
    {
153
        $this->properties["FN;" . $this->encoding] = encode($name);
154
    }
155
156
    /**
157
     *  mise en forme du nom complete
158
     *
159
     *  @param  string  $family         Family name
160
     *  @param  string  $first          First name
161
     *  @param  string  $additional     Additional (e.g. second name, nick name)
162
     *  @param  string  $prefix         Title prefix (e.g. "Mr.", "Ms.", "Prof.")
163
     *  @param  string  $suffix         Suffix (e.g. "sen." for senior, "jun." for junior)
164
     *  @return void
165
     */
166
    public function setName($family = "", $first = "", $additional = "", $prefix = "", $suffix = "")
167
    {
168
        //$this->properties["N;".$this->encoding] = encode($family).";".encode($first).";".encode($additional).";".encode($prefix).";".encode($suffix);
169
        $this->properties["N"] = encode($family) . ";" . encode($first) . ";" . encode($additional) . ";" . encode($prefix) . ";" . encode($suffix);
170
        $this->filename = "$first%20$family.vcf";
171
        if (empty($this->properties["FN"])) {
172
            $this->setFormattedName(trim("$prefix $first $additional $family $suffix"));
173
        }
174
    }
175
176
    /**
177
     *  mise en forme de l'anniversaire
178
     *
179
     *  @param  integer   $date     Date
180
     *  @return void
181
     */
182
    public function setBirthday($date)
183
    {
184
        // $date format is YYYY-MM-DD - RFC 2425 and RFC 2426 for vcard v3
185
        // $date format is YYYYMMDD or ISO8601 for vcard v4
186
        $this->properties["BDAY"] = dol_print_date($date, 'dayxcard');
187
    }
188
189
    /**
190
     *  Address
191
     *
192
     *  @param  string  $postoffice     Postoffice
193
     *  @param  string  $extended       Extended
194
     *  @param  string  $street         Street
195
     *  @param  string  $city           City
196
     *  @param  string  $region         Region
197
     *  @param  string  $zip            Zip
198
     *  @param  string  $country        Country
199
     *  @param  string  $type           Type
200
     *  @param  string  $label          Label
201
     *  @return void
202
     */
203
    public function setAddress($postoffice = "", $extended = "", $street = "", $city = "", $region = "", $zip = "", $country = "", $type = "", $label = "")
204
    {
205
        // $type may be DOM | INTL | POSTAL | PARCEL | HOME | WORK or any combination of these: e.g. "WORK;PARCEL;POSTAL"
206
        $key = "ADR";
207
        if ($type != "") {
208
            $key .= ";" . $type;
209
        }
210
        if ($label != "") {
211
            $key .= ';LABEL="' . encode($label) . '"';
212
        }
213
        $key .= ";" . $this->encoding;
214
        $this->properties[$key] = encode($postoffice) . ";" . encode($extended) . ";" . encode($street) . ";" . encode($city) . ";" . encode($region) . ";" . encode($zip) . ";" . encode($country);
215
216
        //if ($this->properties["LABEL;".$type.";".$this->encoding] == '') {
217
            //$this->setLabel($postoffice, $extended, $street, $city, $region, $zip, $country, $type);
218
        //}
219
    }
220
221
    /**
222
     *  Address (old standard)
223
     *
224
     *  @param  string  $postoffice     Postoffice
225
     *  @param  string  $extended       Extended
226
     *  @param  string  $street         Street
227
     *  @param  string  $city           City
228
     *  @param  string  $region         Region
229
     *  @param  string  $zip            Zip
230
     *  @param  string  $country        Country
231
     *  @param  string  $type           Type
232
     *  @return void
233
     *  @deprecated
234
     */
235
    public function setLabel($postoffice = "", $extended = "", $street = "", $city = "", $region = "", $zip = "", $country = "", $type = "HOME")
236
    {
237
        $label = "";
238
        if ($postoffice != "") {
239
            $label .= "$postoffice\r\n";
240
        }
241
        if ($extended != "") {
242
            $label .= "$extended\r\n";
243
        }
244
        if ($street != "") {
245
            $label .= "$street\r\n";
246
        }
247
        if ($zip != "") {
248
            $label .= "$zip ";
249
        }
250
        if ($city != "") {
251
            $label .= "$city\r\n";
252
        }
253
        if ($region != "") {
254
            $label .= "$region\r\n";
255
        }
256
        if ($country != "") {
257
            $country .= "$country\r\n";
258
        }
259
260
        $this->properties["LABEL;$type;" . $this->encoding] = encode($label);
261
    }
262
263
    /**
264
     *  Add a e-mail address to this vCard
265
     *
266
     *  @param  string  $address        E-mail address
267
     *  @param  string  $type           (optional) The type of the e-mail (typical "PREF" or "INTERNET")
268
     *  @return void
269
     */
270
    public function setEmail($address, $type = "")
271
    {
272
        $key = "EMAIL";
273
        if ($type == "PREF") {
274
            $key .= ";PREF=1";
275
        } elseif (!empty($type)) {
276
            $key .= ";TYPE=" . dol_strtolower($type);
277
        }
278
        $this->properties[$key] = $address;
279
    }
280
281
    /**
282
     *  mise en forme de la note
283
     *
284
     *  @param  string  $note       Note
285
     *  @return void
286
     */
287
    public function setNote($note)
288
    {
289
        $this->properties["NOTE;" . $this->encoding] = encode($note);
290
    }
291
292
    /**
293
     *  mise en forme de la fonction
294
     *
295
     *  @param  string  $title      Title
296
     *  @return void
297
     */
298
    public function setTitle($title)
299
    {
300
        $this->properties["TITLE;" . $this->encoding] = encode($title);
301
    }
302
303
304
    /**
305
     *  mise en forme de la societe
306
     *
307
     *  @param  string  $org        Org
308
     *  @return void
309
     */
310
    public function setOrg($org)
311
    {
312
        $this->properties["ORG;" . $this->encoding] = encode($org);
313
    }
314
315
316
    /**
317
     *  mise en forme du logiciel generateur
318
     *
319
     *  @param  string  $prodid     Prodid
320
     *  @return void
321
     */
322
    public function setProdId($prodid)
323
    {
324
        $this->properties["PRODID"] = encode($prodid);
325
    }
326
327
328
    /**
329
     *  mise en forme du logiciel generateur
330
     *
331
     *  @param  string  $uid    Uid
332
     *  @return void
333
     */
334
    public function setUID($uid)
335
    {
336
        $this->properties["UID"] = encode($uid);
337
    }
338
339
340
    /**
341
     *  mise en forme de l'url
342
     *
343
     *  @param  string  $url        URL
344
     *  @param  string  $type       Type
345
     *  @return void
346
     */
347
    public function setURL($url, $type = "")
348
    {
349
        // $type may be WORK | HOME
350
        $key = "URL";
351
        if ($type != "") {
352
            $key .= ";$type";
353
        }
354
        $this->properties[$key] = $url;
355
    }
356
357
    /**
358
     *  permet d'obtenir une vcard
359
     *
360
     *  @return string
361
     */
362
    public function getVCard()
363
    {
364
        $text = "BEGIN:VCARD\r\n";
365
        $text .= "VERSION:4.0\r\n";     // With V4, all encoding are UTF-8
366
        //$text.= "VERSION:2.1\r\n";
367
        foreach ($this->properties as $key => $value) {
368
            $newkey = preg_replace('/-.*$/', '', $key); // remove suffix -twitter, -facebook, ...
369
            $text .= $newkey . ":" . $value . "\r\n";
370
        }
371
        $text .= "REV:" . date("Ymd") . "T" . date("His") . "Z\r\n";
372
        //$text .= "MAILER: Dolibarr\r\n";
373
        $text .= "END:VCARD\r\n";
374
        return $text;
375
    }
376
377
    /**
378
     *  permet d'obtenir le nom de fichier
379
     *
380
     *  @return string      Filename
381
     */
382
    public function getFileName()
383
    {
384
        return $this->filename;
385
    }
386
387
    /**
388
     * Return a VCARD string
389
     * See RFC https://datatracker.ietf.org/doc/html/rfc6350
390
     *
391
     * @param   Object          $object     Object (User or Contact)
392
     * @param   Societe|null    $company    Company. May be null
393
     * @param   Translate       $langs      Lang object
394
     * @param   string          $urlphoto   Full public URL of photo
395
     * @return  string                      String
396
     */
397
    public function buildVCardString($object, $company, $langs, $urlphoto = '')
398
    {
399
        global $dolibarr_main_instance_unique_id;
400
401
        $this->setProdId('Dolibarr ' . DOL_VERSION);
402
403
        $this->setUID('DOLIBARR-USERID-' . dol_trunc(md5('vcard' . $dolibarr_main_instance_unique_id), 8, 'right', 'UTF-8', 1) . '-' . $object->id);
404
        $this->setName($object->lastname, $object->firstname, "", $object->civility_code, "");
405
        $this->setFormattedName($object->getFullName($langs, 1));
406
407
        if ($urlphoto) {
408
            $mimetype = dol_mimetype($urlphoto);
409
            if ($mimetype) {
410
                $this->setPhoto($mimetype, $urlphoto);
411
            }
412
        }
413
414
        if ($object->office_phone) {
415
            $this->setPhoneNumber($object->office_phone, "TYPE=WORK,VOICE");
416
        }
417
        /* disabled
418
        if ($object->personal_mobile) {
419
            $this->setPhoneNumber($object->personal_mobile, "TYPE=CELL,VOICE");
420
        }*/
421
        if ($object->user_mobile) {
422
            $this->setPhoneNumber($object->user_mobile, "TYPE=CELL,VOICE");
423
        }
424
        if ($object->office_fax) {
425
            $this->setPhoneNumber($object->office_fax, "TYPE=WORK,FAX");
426
        }
427
428
        if (!empty($object->socialnetworks)) {
429
            foreach ($object->socialnetworks as $key => $val) {
430
                if (empty($val)) {  // Disacard social network if empty
431
                    continue;
432
                }
433
                $urlsn = '';
434
                if ($key == 'linkedin') {
435
                    if (!preg_match('/^http/', $val)) {
436
                        $urlsn = 'https://www.' . $key . '.com/company/' . urlencode($val);
437
                    } else {
438
                        $urlsn = $val;
439
                    }
440
                } elseif ($key == 'youtube') {
441
                    if (!preg_match('/^http/', $val)) {
442
                        $urlsn = 'https://www.' . $key . '.com/user/' . urlencode($val);
443
                    } else {
444
                        $urlsn = $val;
445
                    }
446
                } else {
447
                    if (!preg_match('/^http/', $val)) {
448
                        $urlsn = 'https://www.' . $key . '.com/' . urlencode($val);
449
                    } else {
450
                        $urlsn = $val;
451
                    }
452
                }
453
                if ($urlsn) {
454
                    $this->properties["SOCIALPROFILE;TYPE=WORK-" . $key] = $key . ':' . $urlsn;
455
                }
456
            }
457
        }
458
459
        $country = $object->country_code ? $object->country : '';
460
461
        // User address
462
        if (!($object->element != 'user') || getDolUserInt('USER_PUBLIC_SHOW_ADDRESS', 0, $object)) {
463
            if ($object->address || $object->town || $object->state || $object->zip || $object->country) {
464
                $this->setAddress("", "", $object->address, $object->town, $object->state, $object->zip, $country, "");
465
                //$this->setLabel("", "", $object->address, $object->town, $object->state, $object->zip, $country, "TYPE=HOME");
466
            }
467
        }
468
469
        if ($object->email) {
470
            $this->setEmail($object->email, "TYPE=WORK");
471
        }
472
        /* disabled
473
        if ($object->personal_email) {
474
            $this->setEmail($object->personal_email, "TYPE=HOME");
475
        } */
476
        if ($object->note_public) {
477
            $this->setNote($object->note_public);
478
        }
479
        if ($object->job) {
480
            $this->setTitle($object->job);
481
        }
482
483
        // For user, $object->url is not defined
484
        // For contact, $object->url is not defined
485
        if (!empty($object->url)) {
486
            $this->setURL($object->url, "");
487
        }
488
489
        if (is_object($company)) {
490
            // Si user linked to a thirdparty and not a physical people
491
            if ($company->typent_code != 'TE_PRIVATE') {
492
                $this->setOrg($company->name);
493
            }
494
495
            $this->setURL($company->url, "");
496
497
            if ($company->phone && $company->phone != $object->office_phone) {
498
                $this->setPhoneNumber($company->phone, "TYPE=WORK,VOICE");
499
            }
500
            if ($company->fax && $company->fax != $object->office_fax) {
501
                $this->setPhoneNumber($company->fax, "TYPE=WORK,FAX");
502
            }
503
            if ($company->address || $company->town || $company->state || $company->zip || $company->country) {
504
                $this->setAddress("", "", $company->address, $company->town, $company->state, $company->zip, $company->country, "TYPE=WORK");
505
            }
506
507
            if ($company->email && $company->email != $object->email) {
508
                $this->setEmail($company->email, "TYPE=WORK");
509
            }
510
511
            /*
512
            if (!empty($company->socialnetworks)) {
513
                foreach ($company->socialnetworks as $key => $val) {
514
                    $urlsn = '';
515
                    if ($key == 'linkedin') {
516
                        if (!preg_match('/^http/', $val)) {
517
                            $urlsn = 'https://www.'.$key.'.com/company/'.urlencode($val);
518
                        } else {
519
                            $urlsn = $val;
520
                        }
521
                    } elseif ($key == 'youtube') {
522
                        if (!preg_match('/^http/', $val)) {
523
                            $urlsn = 'https://www.'.$key.'.com/user/'.urlencode($val);
524
                        } else {
525
                            $urlsn = $val;
526
                        }
527
                    } else {
528
                        if (!preg_match('/^http/', $val)) {
529
                            $urlsn = 'https://www.'.$key.'.com/'.urlencode($val);
530
                        } else {
531
                            $urlsn = $val;
532
                        }
533
                    }
534
                    if ($urlsn) {
535
                        $this->properties["socialProfile;type=".$key] = $urlsn;
536
                    }
537
                }
538
            }
539
            */
540
        }
541
542
        // Birthday
543
        if (!($object->element != 'user') || getDolUserInt('USER_PUBLIC_SHOW_BIRTH', 0, $object)) {
544
            if ($object->birth) {
545
                $this->setBirthday($object->birth);
546
            }
547
        }
548
549
        // Return VCard string
550
        return $this->getVCard();
551
    }
552
553
554
    /* Example from Microsoft Outlook 2019
555
556
    BEGIN:VCARD
557
    VERSION:2.1
558
559
    N;LANGUAGE=de:surename;forename;secondname;Sir;jun.
560
    FN:Sir surename secondname forename jun.
561
    ORG:Companyname
562
    TITLE:position
563
    TEL;WORK;VOICE:work-phone-number
564
    TEL;HOME;VOICE:private-phone-number
565
    TEL;CELL;VOICE:mobile-phone-number
566
    TEL;WORK;FAX:fax-phone-number
567
    ADR;WORK;PREF:;;street and number;town;region;012345;Deutschland
568
    LABEL;WORK;PREF;ENCODING=QUOTED-PRINTABLE:street and number=0D=0A=
569
    =0D=0A=
570
    012345  town  region
571
    X-MS-OL-DEFAULT-POSTAL-ADDRESS:2
572
    URL;WORK:www.mywebpage.de
573
    EMAIL;PREF;INTERNET:[email protected]
574
    EMAIL;INTERNET:[email protected]
575
    EMAIL;INTERNET:[email protected]
576
    X-MS-IMADDRESS:[email protected]
577
    REV:20200424T104242Z
578
579
    END:VCARD
580
    */
581
}
582