Passed
Branch develop (5cbde9)
by
unknown
26:38
created

Ticket::LibStatut()   D

Complexity

Conditions 40

Size

Total Lines 113
Code Lines 73

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 40
eloc 73
c 0
b 0
f 0
nop 2
dl 0
loc 113
rs 4.1666

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
/* Copyright (C) 2013-2018 Jean-François Ferry <[email protected]>
3
 * Copyright (C) 2016      Christophe Battarel <[email protected]>
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
 */
18
19
/**
20
 *  \file       ticket/class/ticket.class.php
21
 *  \ingroup    ticket
22
 *  \brief      Class file for object ticket
23
 */
24
25
// Put here all includes required by your class file
26
require_once DOL_DOCUMENT_ROOT . "/core/class/commonobject.class.php";
27
require_once DOL_DOCUMENT_ROOT . '/fichinter/class/fichinter.class.php';
28
require_once DOL_DOCUMENT_ROOT . '/core/lib/ticket.lib.php';
29
30
31
/**
32
 *    Class to manage ticket
33
 */
34
class Ticket extends CommonObject
35
{
36
    /**
37
     * @var string ID to identify managed object
38
     */
39
    public $element = 'ticket';
40
41
    /**
42
     * @var string Name of table without prefix where object is stored
43
     */
44
    public $table_element = 'ticket';
45
46
    /**
47
     * @var string Name of field for link to tickets
48
     */
49
    public $fk_element='fk_ticket';
50
51
    /**
52
     * @var int  Does ticketcore support multicompany module ? 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
53
     */
54
    public $ismultientitymanaged = 1;
55
56
    /**
57
     * @var int  Does ticketcore support extrafields ? 0=No, 1=Yes
58
     */
59
    public $isextrafieldmanaged = 1;
60
61
    /**
62
     * @var string String with name of icon for ticketcore. Must be the part after the 'object_' into object_ticketcore.png
63
     */
64
    public $picto = 'ticket';
65
66
67
    /**
68
     * @var string Hash to identify ticket publically
69
     */
70
    public $track_id;
71
72
    /**
73
     * @var int Thirdparty ID
74
     */
75
    public $fk_soc;
76
77
    /**
78
     * @var int Project ID
79
     */
80
    public $fk_project;
81
82
    /**
83
     * @var string Person email who have create ticket
84
     */
85
    public $origin_email;
86
87
    /**
88
     * @var int User id who have create ticket
89
     */
90
    public $fk_user_create;
91
92
    /**
93
     * @var int User id who have ticket assigned
94
     */
95
    public $fk_user_assign;
96
97
    /**
98
     * var string Ticket subject
99
     */
100
    public $subject;
101
102
    /**
103
     * @var string Ticket message
104
     */
105
    public $message;
106
107
    /**
108
     * @var int  Ticket statut
109
     */
110
    public $fk_statut;
111
112
    /**
113
     * @var string State resolution
114
     */
115
    public $resolution;
116
117
    /**
118
     * @var int Progress in percent
119
     */
120
    public $progress;
121
122
    /**
123
     * @var int Duration for ticket
124
     */
125
    public $timing;
126
127
    /**
128
     * @var string Type code
129
     */
130
    public $type_code;
131
132
    /**
133
     * @var string Category code
134
     */
135
    public $category_code;
136
137
    /**
138
     * @var string Severity code
139
     */
140
    public $severity_code;
141
142
    /**
143
     * @var int Création date
144
     */
145
    public $datec = '';
146
147
    /**
148
     * @var int Read date
149
     */
150
    public $date_read = '';
151
152
    /**
153
     * @var int Close ticket date
154
     */
155
    public $date_close = '';
156
157
    /**
158
     * @var array cache_types_tickets
159
     */
160
    public $cache_types_tickets;
161
162
    /**
163
     * @var array tickets categories
164
     */
165
    public $cache_category_tickets;
166
167
    /**
168
     * @var int Notify tiers at create
169
     */
170
    public $notify_tiers_at_create;
171
172
    public $lines;
173
174
    /**
175
     * @var string Regex pour les images
176
     */
177
    public $regeximgext = '\.jpg|\.jpeg|\.bmp|\.gif|\.png|\.tiff';
178
179
    public $fields=array(
180
        'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'position'=>1, 'visible'=>-2, 'enabled'=>1, 'position'=>1, 'notnull'=>1, 'index'=>1, 'comment'=>"Id"),
181
    	'entity' => array('type'=>'integer', 'label'=>'Entity', 'visible'=>0, 'enabled'=>1, 'position'=>5, 'notnull'=>1, 'index'=>1),
182
    	'ref' => array('type'=>'varchar(128)', 'label'=>'Ref', 'visible'=>1, 'enabled'=>1, 'position'=>10, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>"Reference of object", 'css'=>''),
183
	    'track_id' => array('type'=>'varchar(255)', 'label'=>'TicketTrackId', 'visible'=>-2, 'enabled'=>1, 'position'=>11, 'notnull'=>-1, 'searchall'=>1, 'help'=>"Help text"),
184
	    'fk_user_create' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'Author', 'visible'=>1, 'enabled'=>1, 'position'=>15, 'notnull'=>1, 'css'=>'nowraponall'),
185
    	'origin_email' => array('type'=>'mail', 'label'=>'OriginEmail', 'visible'=>-2, 'enabled'=>1, 'position'=>16, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>"Reference of object"),
186
    	'subject' => array('type'=>'varchar(255)', 'label'=>'Subject', 'visible'=>1, 'enabled'=>1, 'position'=>18, 'notnull'=>-1, 'searchall'=>1, 'help'=>"", 'css'=>'maxwidth75'),
187
    	'type_code' => array('type'=>'varchar(32)', 'label'=>'Type', 'visible'=>1, 'enabled'=>1, 'position'=>20, 'notnull'=>-1, 'searchall'=>1, 'help'=>"", 'css'=>'maxwidth100'),
188
    	'category_code' => array('type'=>'varchar(32)', 'label'=>'TicketGroup', 'visible'=>-1, 'enabled'=>1, 'position'=>21, 'notnull'=>-1, 'help'=>"", 'css'=>'maxwidth100'),
189
	    'severity_code' => array('type'=>'varchar(32)', 'label'=>'Severity', 'visible'=>1, 'enabled'=>1, 'position'=>22, 'notnull'=>-1, 'help'=>"", 'css'=>'maxwidth100'),
190
    	'fk_soc' => array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'visible'=>1, 'enabled'=>1, 'position'=>50, 'notnull'=>-1, 'index'=>1, 'searchall'=>1, 'help'=>"LinkToThirparty"),
191
	    'notify_tiers_at_create' => array('type'=>'integer', 'label'=>'NotifyThirdparty', 'visible'=>-1, 'enabled'=>0, 'position'=>51, 'notnull'=>1, 'index'=>1),
192
    	'fk_project' => array('type'=>'integer:Project:projet/class/project.class.php', 'label'=>'Project', 'visible'=>-1, 'enabled'=>1, 'position'=>52, 'notnull'=>-1, 'index'=>1, 'help'=>"LinkToProject"),
193
        'timing' => array('type'=>'varchar(20)', 'label'=>'Timing', 'visible'=>-1, 'enabled'=>1, 'position'=>42, 'notnull'=>-1, 'help'=>""),
194
        'datec' => array('type'=>'datetime', 'label'=>'DateCreation', 'visible'=>1, 'enabled'=>1, 'position'=>500, 'notnull'=>1),
195
        'date_read' => array('type'=>'datetime', 'label'=>'TicketReadOn', 'visible'=>1, 'enabled'=>1, 'position'=>500, 'notnull'=>1),
196
        'fk_user_assign' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'AssignedTo', 'visible'=>1, 'enabled'=>1, 'position'=>505, 'notnull'=>1),
197
        'date_close' => array('type'=>'datetime', 'label'=>'TicketCloseOn', 'visible'=>-1, 'enabled'=>1, 'position'=>510, 'notnull'=>1),
198
        'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'visible'=>-1, 'enabled'=>1, 'position'=>520, 'notnull'=>1),
199
        'message' => array('type'=>'text', 'label'=>'Message', 'visible'=>-2, 'enabled'=>1, 'position'=>540, 'notnull'=>-1,),
200
        'progress' => array('type'=>'varchar(100)', 'label'=>'Progression', 'visible'=>-1, 'enabled'=>1, 'position'=>540, 'notnull'=>-1, 'css'=>'right', 'help'=>""),
201
        'resolution' => array('type'=>'integer', 'label'=>'Resolution', 'visible'=>-1, 'enabled'=>1, 'position'=>550, 'notnull'=>1),
202
        'fk_statut' => array('type'=>'integer', 'label'=>'Status', 'visible'=>1, 'enabled'=>1, 'position'=>600, 'notnull'=>1, 'index'=>1, 'arrayofkeyval'=>array(0 => 'Unread', 1 => 'Read', 3 => 'Answered', 4 => 'Assigned', 5 => 'InProgress', 6 => 'Waiting', 8 => 'Closed', 9 => 'Deleted'))
203
    );
204
205
    /**
206
     * Status
207
     */
208
    const STATUS_NOT_READ = 0;
209
    const STATUS_READ = 1;
210
    const STATUS_ASSIGNED = 2;
211
    const STATUS_IN_PROGRESS = 3;
212
    const STATUS_NEED_MORE_INFO = 5;
213
    const STATUS_WAITING = 7;
214
    const STATUS_CLOSED = 8;
215
    const STATUS_CANCELED = 9;
216
217
218
    /**
219
     *  Constructor
220
     *
221
     *  @param DoliDb $db Database handler
222
     */
223
    public function __construct($db)
224
    {
225
        $this->db = $db;
226
227
        $this->statuts_short = array(self::STATUS_NOT_READ => 'Unread', self::STATUS_READ => 'Read', self::STATUS_ASSIGNED => 'Assigned', self::STATUS_IN_PROGRESS => 'InProgress', self::STATUS_NEED_MORE_INFO => 'NeedMoreInformation', self::STATUS_WAITING => 'Suspended', self::STATUS_CLOSED => 'Closed', self::STATUS_CANCELED => 'Canceled');
228
        $this->statuts       = array(self::STATUS_NOT_READ => 'Unread', self::STATUS_READ => 'Read', self::STATUS_ASSIGNED => 'Assigned', self::STATUS_IN_PROGRESS => 'InProgress', self::STATUS_NEED_MORE_INFO => 'NeedMoreInformation', self::STATUS_WAITING => 'Suspended', self::STATUS_CLOSED => 'Closed', self::STATUS_CANCELED => 'Canceled');
229
    }
230
231
    /**
232
     *    Check properties of ticket are ok (like ref, track_id, ...).
233
     *    All properties must be already loaded on object (this->ref, this->track_id, ...).
234
     *
235
     *    @return int        0 if OK, <0 if KO
236
     */
237
    private function verify()
238
    {
239
        $this->errors = array();
240
241
        $result = 0;
242
243
        // Clean parameters
244
        if (isset($this->ref)) {
245
            $this->ref = trim($this->ref);
246
        }
247
248
        if (isset($this->track_id)) {
249
            $this->track_id = trim($this->track_id);
250
        }
251
252
        if (isset($this->fk_soc)) {
253
            $this->fk_soc = trim($this->fk_soc);
1 ignored issue
show
Documentation Bug introduced by
The property $fk_soc was declared of type integer, but trim($this->fk_soc) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
254
        }
255
256
        if (isset($this->fk_project)) {
257
            $this->fk_project = trim($this->fk_project);
1 ignored issue
show
Documentation Bug introduced by
The property $fk_project was declared of type integer, but trim($this->fk_project) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
258
        }
259
260
        if (isset($this->origin_email)) {
261
            $this->origin_email = trim($this->origin_email);
262
        }
263
264
        if (isset($this->fk_user_create)) {
265
            $this->fk_user_create = trim($this->fk_user_create);
1 ignored issue
show
Documentation Bug introduced by
The property $fk_user_create was declared of type integer, but trim($this->fk_user_create) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
266
        }
267
268
        if (isset($this->fk_user_assign)) {
269
            $this->fk_user_assign = trim($this->fk_user_assign);
1 ignored issue
show
Documentation Bug introduced by
The property $fk_user_assign was declared of type integer, but trim($this->fk_user_assign) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
270
        }
271
272
        if (isset($this->subject)) {
273
            $this->subject = trim($this->subject);
274
        }
275
276
        if (isset($this->message)) {
277
            $this->message = trim($this->message);
278
        }
279
280
        if (isset($this->fk_statut)) {
281
            $this->fk_statut = trim($this->fk_statut);
282
        }
283
284
        if (isset($this->resolution)) {
285
            $this->resolution = trim($this->resolution);
286
        }
287
288
        if (isset($this->progress)) {
289
            $this->progress = trim($this->progress);
290
        }
291
292
        if (isset($this->timing)) {
293
            $this->timing = trim($this->timing);
294
        }
295
296
        if (isset($this->type_code)) {
297
            $this->type_code = trim($this->type_code);
298
        }
299
300
        if (isset($this->category_code)) {
301
            $this->category_code = trim($this->category_code);
302
        }
303
304
        if (isset($this->severity_code)) {
305
            $this->severity_code = trim($this->severity_code);
306
        }
307
308
        if (empty($this->ref)) {
309
            $this->errors[] = 'ErrorBadRef';
310
            dol_syslog(get_class($this) . "::create error -1 ref null", LOG_ERR);
311
            $result = -1;
312
        }
313
314
        return $result;
315
    }
316
317
    /**
318
     *  Create object into database
319
     *
320
     *  @param  User $user      User that creates
321
     *  @param  int  $notrigger 0=launch triggers after, 1=disable triggers
322
     *  @return int                      <0 if KO, Id of created object if OK
323
     */
324
    public function create($user, $notrigger = 0)
325
    {
326
        global $conf, $langs;
327
        $error = 0;
328
329
        // Clean parameters
330
        $this->datec = dol_now();
331
        if (empty($this->track_id)) $this->track_id = generate_random_id(16);
0 ignored issues
show
Documentation Bug introduced by
It seems like generate_random_id(16) of type void is incompatible with the declared type string of property $track_id.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
Bug introduced by
Are you sure the assignment to $this->track_id is correct as generate_random_id(16) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
332
333
        // Check more parameters
334
        // If error, this->errors[] is filled
335
        $result = $this->verify();
336
337
        if ($result >= 0) {
338
            // Insert request
339
            $sql = "INSERT INTO " . MAIN_DB_PREFIX . "ticket(";
340
            $sql .= "ref,";
341
            $sql .= "track_id,";
342
            $sql .= "fk_soc,";
343
            $sql .= "fk_project,";
344
            $sql .= "origin_email,";
345
            $sql .= "fk_user_create,";
346
            $sql .= "fk_user_assign,";
347
            $sql .= "subject,";
348
            $sql .= "message,";
349
            $sql .= "fk_statut,";
350
            $sql .= "resolution,";
351
            $sql .= "progress,";
352
            $sql .= "timing,";
353
            $sql .= "type_code,";
354
            $sql .= "category_code,";
355
            $sql .= "severity_code,";
356
            $sql .= "datec,";
357
            $sql .= "date_read,";
358
            $sql .= "date_close,";
359
            $sql .= "entity,";
360
            $sql .= "notify_tiers_at_create";
361
            $sql .= ") VALUES (";
362
            $sql .= " " . (!isset($this->ref) ? '' : "'" . $this->db->escape($this->ref) . "'") . ",";
363
            $sql .= " " . (!isset($this->track_id) ? 'NULL' : "'" . $this->db->escape($this->track_id) . "'") . ",";
364
            $sql .= " " . ($this->fk_soc > 0 ? $this->db->escape($this->fk_soc) : "null") . ",";
365
            $sql .= " " . ($this->fk_project > 0 ? $this->db->escape($this->fk_project) : "null") . ",";
366
            $sql .= " " . (!isset($this->origin_email) ? 'NULL' : "'" . $this->db->escape($this->origin_email) . "'") . ",";
367
            $sql .= " " . ($this->fk_user_create > 0 ? $this->fk_user_create : ($user->id > 0 ? $user->id : 'NULL')) . ",";
368
            $sql .= " " . ($this->fk_user_assign > 0 ? $this->fk_user_assign : 'NULL') . ",";
369
            $sql .= " " . (!isset($this->subject) ? 'NULL' : "'" . $this->db->escape($this->subject) . "'") . ",";
370
            $sql .= " " . (!isset($this->message) ? 'NULL' : "'" . $this->db->escape($this->message) . "'") . ",";
371
            $sql .= " " . (!isset($this->fk_statut) ? '0' : "'" . $this->db->escape($this->fk_statut) . "'") . ",";
372
            $sql .= " " . (!isset($this->resolution) ? 'NULL' : "'" . $this->db->escape($this->resolution) . "'") . ",";
373
            $sql .= " " . (!isset($this->progress) ? '0' : "'" . $this->db->escape($this->progress) . "'") . ",";
374
            $sql .= " " . (!isset($this->timing) ? 'NULL' : "'" . $this->db->escape($this->timing) . "'") . ",";
375
            $sql .= " " . (!isset($this->type_code) ? 'NULL' : "'" . $this->db->escape($this->type_code) . "'") . ",";
376
            $sql .= " " . (!isset($this->category_code) ? 'NULL' : "'" . $this->db->escape($this->category_code) . "'") . ",";
377
            $sql .= " " . (!isset($this->severity_code) ? 'NULL' : "'" . $this->db->escape($this->severity_code) . "'") . ",";
378
            $sql .= " " . (!isset($this->datec) || dol_strlen($this->datec) == 0 ? 'NULL' : "'" . $this->db->idate($this->datec) . "'") . ",";
379
            $sql .= " " . (!isset($this->date_read) || dol_strlen($this->date_read) == 0 ? 'NULL' : "'" . $this->db->idate($this->date_read) . "'") . ",";
380
            $sql .= " " . (!isset($this->date_close) || dol_strlen($this->date_close) == 0 ? 'NULL' : "'" . $this->db->idate($this->date_close) . "'") . "";
381
            $sql .= ", " . $conf->entity;
382
            $sql .= ", " . (!isset($this->notify_tiers_at_create) ? '1' : "'" . $this->db->escape($this->notify_tiers_at_create) . "'");
383
            $sql .= ")";
384
385
            $this->db->begin();
386
387
            dol_syslog(get_class($this) . "::create sql=" . $sql, LOG_DEBUG);
388
            $resql = $this->db->query($sql);
389
            if (!$resql) {
390
                $error++;
391
                $this->errors[] = "Error " . $this->db->lasterror();
392
            }
393
394
            if (!$error) {
395
                $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . "ticket");
396
397
                if (!$notrigger) {
398
                    // Call trigger
399
                    $result=$this->call_trigger('TICKET_CREATE', $user);
400
                    if ($result < 0) {
401
                        $error++;
402
                    }
403
                    // End call triggers
404
                }
405
            }
406
407
            //Update extrafield
408
            if (! $error) {
409
                if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) { // For avoid conflicts if trigger used
410
                    $result = $this->insertExtraFields();
411
                    if ($result < 0) {
412
                        $error++;
413
                    }
414
                }
415
            }
416
417
            // Commit or rollback
418
            if ($error) {
419
                foreach ($this->errors as $errmsg) {
420
                    dol_syslog(get_class($this) . "::create " . $errmsg, LOG_ERR);
421
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
422
                }
423
                $this->db->rollback();
424
                return -1 * $error;
425
            } else {
426
                $this->db->commit();
427
                return $this->id;
428
            }
429
        } else {
430
            $this->db->rollback();
431
            dol_syslog(get_class($this) . "::Create fails verify " . join(',', $this->errors), LOG_WARNING);
432
            return -3;
433
        }
434
    }
435
436
    /**
437
     *  Load object in memory from the database
438
     *
439
     *  @param  int        	$id    		Id object
440
     *  @param	string		$ref		Ref
441
     *  @param	string		$track_id	Track id, a hash like ref
442
     *  @return int              		<0 if KO, >0 if OK
443
     */
444
    public function fetch($id = '', $ref = '', $track_id = '')
445
    {
446
        global $langs;
447
448
        // Check parameters
449
        if (! $id && ! $track_id && ! $ref) {
450
            $this->error = 'ErrorWrongParameters';
451
            dol_print_error(get_class($this) . "::fetch " . $this->error);
452
            return -1;
453
        }
454
455
        $sql = "SELECT";
456
        $sql .= " t.rowid,";
457
        $sql .= " t.ref,";
458
        $sql .= " t.track_id,";
459
        $sql .= " t.fk_soc,";
460
        $sql .= " t.fk_project,";
461
        $sql .= " t.origin_email,";
462
        $sql .= " t.fk_user_create,";
463
        $sql .= " t.fk_user_assign,";
464
        $sql .= " t.subject,";
465
        $sql .= " t.message,";
466
        $sql .= " t.fk_statut,";
467
        $sql .= " t.resolution,";
468
        $sql .= " t.progress,";
469
        $sql .= " t.timing,";
470
        $sql .= " t.type_code,";
471
        $sql .= " t.category_code,";
472
        $sql .= " t.severity_code,";
473
        $sql .= " t.datec,";
474
        $sql .= " t.date_read,";
475
        $sql .= " t.date_close,";
476
        $sql .= " t.tms";
477
        $sql .= ", type.code as type_code, type.label as type_label, category.code as category_code, category.label as category_label, severity.code as severity_code, severity.label as severity_label";
478
        $sql .= " FROM " . MAIN_DB_PREFIX . "ticket as t";
479
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticket_type as type ON type.code=t.type_code";
480
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticket_category as category ON category.code=t.category_code";
481
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticket_severity as severity ON severity.code=t.severity_code";
482
483
        if ($id) {
484
            $sql .= " WHERE t.rowid = " . $this->db->escape($id);
485
        } else {
486
            $sql .= " WHERE t.entity IN (" . getEntity($this->element, 1) . ")";
487
            if ($track_id) {
488
                $sql .= " AND t.track_id = '" . $this->db->escape($track_id) . "'";
489
            } elseif ($ref) {
490
                $sql .= " AND t.ref = '" . $this->db->escape($ref) . "'";
491
            }
492
        }
493
494
        dol_syslog(get_class($this) . "::fetch sql=" . $sql, LOG_DEBUG);
495
        $resql = $this->db->query($sql);
496
        if ($resql) {
497
            if ($this->db->num_rows($resql))
498
            {
499
                $obj = $this->db->fetch_object($resql);
500
501
                $this->id = $obj->rowid;
502
                $this->ref = $obj->ref;
503
                $this->track_id = $obj->track_id;
504
                $this->fk_soc = $obj->fk_soc;
505
                $this->socid = $obj->fk_soc; // for fetch_thirdparty() method
506
                $this->fk_project = $obj->fk_project;
507
                $this->origin_email = $obj->origin_email;
508
                $this->fk_user_create = $obj->fk_user_create;
509
                $this->fk_user_assign = $obj->fk_user_assign;
510
                $this->subject = $obj->subject;
511
                $this->message = $obj->message;
512
                $this->fk_statut = $obj->fk_statut;
513
                $this->resolution = $obj->resolution;
514
                $this->progress = $obj->progress;
515
                $this->timing = $obj->timing;
516
517
                $this->type_code = $obj->type_code;
518
                // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
519
                $label_type = ($langs->trans("TicketTypeShort" . $obj->type_code) != ("TicketTypeShort" . $obj->type_code) ? $langs->trans("TicketTypeShort" . $obj->type_code) : ($obj->type_label != '-' ? $obj->type_label : ''));
520
                $this->type_label = $label_type;
521
522
                $this->category_code = $obj->category_code;
523
                // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
524
                $label_category = ($langs->trans("TicketCategoryShort" . $obj->category_code) != ("TicketCategoryShort" . $obj->category_code) ? $langs->trans("TicketCategoryShort" . $obj->category_code) : ($obj->category_label != '-' ? $obj->category_label : ''));
525
                $this->category_label = $label_category;
526
527
                $this->severity_code = $obj->severity_code;
528
                // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
529
                $label_severity = ($langs->trans("TicketSeverityShort" . $obj->severity_code) != ("TicketSeverityShort" . $obj->severity_code) ? $langs->trans("TicketSeverityShort" . $obj->severity_code) : ($obj->severity_label != '-' ? $obj->severity_label : ''));
530
                $this->severity_label = $label_severity;
531
532
                $this->datec = $this->db->jdate($obj->datec);
533
                $this->date_creation = $this->db->jdate($obj->datec);
534
                $this->date_read = $this->db->jdate($obj->date_read);
535
                $this->date_validation = $this->db->jdate($obj->date_read);
536
                $this->date_close = $this->db->jdate($obj->date_close);
537
                $this->tms = $this->db->jdate($obj->tms);
538
                $this->date_modification = $this->db->jdate($obj->tms);
539
540
                $this->fetch_optionals();
541
542
                $this->db->free($resql);
543
                return 1;
544
            }
545
            else
546
            {
547
                return 0;
548
            }
549
        } else {
550
            $this->error = "Error " . $this->db->lasterror();
551
            dol_syslog(get_class($this) . "::fetch " . $this->error, LOG_ERR);
552
            return -1;
553
        }
554
    }
555
556
    /**
557
     * Load all objects in memory from database
558
     *
559
     * @param  User   $user      User for action
560
     * @param  string $sortorder Sort order
561
     * @param  string $sortfield Sort field
562
     * @param  int    $limit     page number
563
     * @param  int    $offset    Offset for query
564
     * @param  int    $arch      archive or not (not used)
565
     * @param  array  $filter    Filter for query
566
     *                           output
567
     * @return int <0 if KO, >0 if OK
568
     */
569
    public function fetchAll($user, $sortorder = 'ASC', $sortfield = 't.datec', $limit = '', $offset = 0, $arch = '', $filter = '')
570
    {
571
        global $langs;
572
573
        $extrafields = new ExtraFields($this->db);
574
575
        // fetch optionals attributes and labels
576
        $extralabels = $extrafields->fetch_name_optionals_label($this->element);
577
578
        $sql = "SELECT";
579
        $sql .= " t.rowid,";
580
        $sql .= " t.ref,";
581
        $sql .= " t.track_id,";
582
        $sql .= " t.fk_soc,";
583
        $sql .= " t.fk_project,";
584
        $sql .= " t.origin_email,";
585
        $sql .= " t.fk_user_create, uc.lastname as user_create_lastname, uc.firstname as user_create_firstname,";
586
        $sql .= " t.fk_user_assign, ua.lastname as user_assign_lastname, ua.firstname as user_assign_firstname,";
587
        $sql .= " t.subject,";
588
        $sql .= " t.message,";
589
        $sql .= " t.fk_statut,";
590
        $sql .= " t.resolution,";
591
        $sql .= " t.progress,";
592
        $sql .= " t.timing,";
593
        $sql .= " t.type_code,";
594
        $sql .= " t.category_code,";
595
        $sql .= " t.severity_code,";
596
        $sql .= " t.datec,";
597
        $sql .= " t.date_read,";
598
        $sql .= " t.date_close,";
599
        $sql .= " t.tms";
600
        $sql .= ", type.label as type_label, category.label as category_label, severity.label as severity_label";
601
        // Add fields for extrafields
602
        foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
603
            $sql .= ($extrafields->attributes[$this->table_element]['type'][$key] != 'separate' ? ",ef." . $key . ' as options_' . $key : '');
604
        }
605
        $sql .= " FROM " . MAIN_DB_PREFIX . "ticket as t";
606
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticket_type as type ON type.code=t.type_code";
607
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticket_category as category ON category.code=t.category_code";
608
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticket_severity as severity ON severity.code=t.severity_code";
609
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON s.rowid=t.fk_soc";
610
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "user as uc ON uc.rowid=t.fk_user_create";
611
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "user as ua ON ua.rowid=t.fk_user_assign";
612
        if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label'])) {
613
            $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "ticket_extrafields as ef on (t.rowid = ef.fk_object)";
614
        }
615
        if (!$user->rights->societe->client->voir && !$user->socid) {
616
            $sql .= ", " . MAIN_DB_PREFIX . "societe_commerciaux as sc";
617
        }
618
619
        $sql .= " WHERE t.entity IN (" . getEntity('ticket') . ")";
620
621
        // Manage filter
622
        if (!empty($filter)) {
623
            foreach ($filter as $key => $value) {
624
                if (strpos($key, 'date')) { // To allow $filter['YEAR(s.dated)']=>$year
625
                    $sql .= ' AND ' . $key . ' = \'' . $value . '\'';
626
                } elseif (($key == 't.fk_user_assign') || ($key == 't.type_code') || ($key == 't.category_code') || ($key == 't.severity_code') || ($key == 't.fk_soc')) {
627
                    $sql .= " AND " . $key . " = '" . $this->db->escape($value) ."'";
628
                } elseif ($key == 't.fk_statut') {
629
                    if (is_array($value) && count($value) > 0) {
630
                        $sql .= 'AND ' . $key . ' IN (' . implode(',', $value) . ')';
631
                    } else {
632
                        $sql .= ' AND ' . $key . ' = ' . $this->db->escape($value);
633
                    }
634
                } else {
635
                    $sql .= ' AND ' . $key . ' LIKE \'%' . $value . '%\'';
636
                }
637
            }
638
        }
639
        if (!$user->rights->societe->client->voir && !$user->socid) {
640
            $sql .= " AND t.fk_soc = sc.fk_soc AND sc.fk_user = " . $user->id;
641
        } elseif ($user->socid) {
642
            $sql .= " AND t.fk_soc = " . $user->socid;
643
        }
644
645
        $sql .= " ORDER BY " . $sortfield . ' ' . $sortorder;
646
        if (!empty($limit)) {
647
            $sql .= ' ' . $this->db->plimit($limit + 1, $offset);
648
        }
649
650
        dol_syslog(get_class($this) . "::fetch_all sql=" . $sql, LOG_DEBUG);
651
        $resql = $this->db->query($sql);
652
653
        if ($resql) {
654
            $this->lines = array();
655
656
            $num = $this->db->num_rows($resql);
657
            $i = 0;
658
659
            if ($num) {
660
                while ($i < $num) {
661
                    $obj = $this->db->fetch_object($resql);
662
663
                    $line = new TicketsLine();
664
665
                    $line->rowid = $obj->rowid;
666
                    $line->ref = $obj->ref;
667
                    $line->track_id = $obj->track_id;
668
                    $line->fk_soc = $obj->fk_soc;
669
                    $line->fk_project = $obj->fk_project;
670
                    $line->origin_email = $obj->origin_email;
671
672
                    $line->fk_user_create = $obj->fk_user_create;
673
                    $line->user_create_lastname = $obj->user_create_lastname;
674
                    $line->user_create_firstname = $obj->user_create_firstname;
675
676
                    $line->fk_user_assign = $obj->fk_user_assign;
677
                    $line->user_assign_lastname = $obj->user_assign_lastname;
678
                    $line->user_assign_firstname = $obj->user_assign_firstname;
679
680
                    $line->subject = $obj->subject;
681
                    $line->message = $obj->message;
682
                    $line->fk_statut = $obj->fk_statut;
683
                    $line->resolution = $obj->resolution;
684
                    $line->progress = $obj->progress;
685
                    $line->timing = $obj->timing;
686
687
                    // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
688
                    $label_type = ($langs->trans("TicketTypeShort" . $obj->type_code) != ("TicketTypeShort" . $obj->type_code) ? $langs->trans("TicketTypeShort" . $obj->type_code) : ($obj->type_label != '-' ? $obj->type_label : ''));
689
                    $line->type_label = $label_type;
690
691
                    $this->category_code = $obj->category_code;
692
                    // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
693
                    $label_category = ($langs->trans("TicketCategoryShort" . $obj->category_code) != ("TicketCategoryShort" . $obj->category_code) ? $langs->trans("TicketCategoryShort" . $obj->category_code) : ($obj->category_label != '-' ? $obj->category_label : ''));
694
                    $line->category_label = $label_category;
695
696
                    $this->severity_code = $obj->severity_code;
697
                    // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
698
                    $label_severity = ($langs->trans("TicketSeverityShort" . $obj->severity_code) != ("TicketSeverityShort" . $obj->severity_code) ? $langs->trans("TicketSeverityShort" . $obj->severity_code) : ($obj->severity_label != '-' ? $obj->severity_label : ''));
699
                    $line->severity_label = $label_severity;
700
701
                    $line->datec = $this->db->jdate($obj->datec);
702
                    $line->date_read = $this->db->jdate($obj->date_read);
703
                    $line->date_close = $this->db->jdate($obj->date_close);
704
705
                    // Extra fields
706
                    if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label'])) {
707
                        foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
708
                            $tmpkey = 'options_' . $key;
709
                            $line->{$tmpkey} = $obj->$tmpkey;
710
                        }
711
                    }
712
713
                    $this->lines[$i] = $line;
714
                    $i++;
715
                }
716
            }
717
            $this->db->free($resql);
718
            return $num;
719
        } else {
720
            $this->error = "Error " . $this->db->lasterror();
721
            dol_syslog(get_class($this) . "::fetch_all " . $this->error, LOG_ERR);
722
            return -1;
723
        }
724
    }
725
726
    /**
727
     *  Update object into database
728
     *
729
     *  @param  User $user      User that modifies
730
     *  @param  int  $notrigger 0=launch triggers after, 1=disable triggers
731
     *  @return int                     <0 if KO, >0 if OK
732
     */
733
    public function update($user = 0, $notrigger = 0)
734
    {
735
        global $conf, $langs, $hookmanager;
736
        $error = 0;
737
738
        // Clean parameters
739
        if (isset($this->ref)) {
740
            $this->ref = trim($this->ref);
741
        }
742
743
        if (isset($this->track_id)) {
744
            $this->track_id = trim($this->track_id);
745
        }
746
747
        if (isset($this->fk_soc)) {
748
            $this->fk_soc = trim($this->fk_soc);
1 ignored issue
show
Documentation Bug introduced by
The property $fk_soc was declared of type integer, but trim($this->fk_soc) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
749
        }
750
751
        if (isset($this->fk_project)) {
752
            $this->fk_project = trim($this->fk_project);
1 ignored issue
show
Documentation Bug introduced by
The property $fk_project was declared of type integer, but trim($this->fk_project) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
753
        }
754
755
        if (isset($this->origin_email)) {
756
            $this->origin_email = trim($this->origin_email);
757
        }
758
759
        if (isset($this->fk_user_create)) {
760
            $this->fk_user_create = trim($this->fk_user_create);
1 ignored issue
show
Documentation Bug introduced by
The property $fk_user_create was declared of type integer, but trim($this->fk_user_create) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
761
        }
762
763
        if (isset($this->fk_user_assign)) {
764
            $this->fk_user_assign = trim($this->fk_user_assign);
1 ignored issue
show
Documentation Bug introduced by
The property $fk_user_assign was declared of type integer, but trim($this->fk_user_assign) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
765
        }
766
767
        if (isset($this->subject)) {
768
            $this->subject = trim($this->subject);
769
        }
770
771
        if (isset($this->message)) {
772
            $this->message = trim($this->message);
773
        }
774
775
        if (isset($this->fk_statut)) {
776
            $this->fk_statut = trim($this->fk_statut);
777
        }
778
779
        if (isset($this->resolution)) {
780
            $this->resolution = trim($this->resolution);
781
        }
782
783
        if (isset($this->progress)) {
784
            $this->progress = trim($this->progress);
785
        }
786
787
        if (isset($this->timing)) {
788
            $this->timing = trim($this->timing);
789
        }
790
791
        if (isset($this->type_code)) {
792
            $this->timing = trim($this->type_code);
793
        }
794
795
        if (isset($this->category_code)) {
796
            $this->timing = trim($this->category_code);
797
        }
798
799
        if (isset($this->severity_code)) {
800
            $this->timing = trim($this->severity_code);
801
        }
802
803
        // Check parameters
804
        // Put here code to add a control on parameters values
805
        // Update request
806
        $sql = "UPDATE " . MAIN_DB_PREFIX . "ticket SET";
807
        $sql .= " ref=" . (isset($this->ref) ? "'" . $this->db->escape($this->ref) . "'" : "") . ",";
808
        $sql .= " track_id=" . (isset($this->track_id) ? "'" . $this->db->escape($this->track_id) . "'" : "null") . ",";
809
        $sql .= " fk_soc=" . (isset($this->fk_soc) ? "'" . $this->db->escape($this->fk_soc) . "'" : "null") . ",";
810
        $sql .= " fk_project=" . (isset($this->fk_project) ? "'" . $this->db->escape($this->fk_project) . "'" : "null") . ",";
811
        $sql .= " origin_email=" . (isset($this->origin_email) ? "'" . $this->db->escape($this->origin_email) . "'" : "null") . ",";
812
        $sql .= " fk_user_create=" . (isset($this->fk_user_create) ? $this->fk_user_create : "null") . ",";
813
        $sql .= " fk_user_assign=" . (isset($this->fk_user_assign) ? $this->fk_user_assign : "null") . ",";
814
        $sql .= " subject=" . (isset($this->subject) ? "'" . $this->db->escape($this->subject) . "'" : "null") . ",";
815
        $sql .= " message=" . (isset($this->message) ? "'" . $this->db->escape($this->message) . "'" : "null") . ",";
816
        $sql .= " fk_statut=" . (isset($this->fk_statut) ? $this->fk_statut : "null") . ",";
817
        $sql .= " resolution=" . (isset($this->resolution) ? $this->resolution : "null") . ",";
818
        $sql .= " progress=" . (isset($this->progress) ? "'" . $this->db->escape($this->progress) . "'" : "null") . ",";
819
        $sql .= " timing=" . (isset($this->timing) ? "'" . $this->db->escape($this->timing) . "'" : "null") . ",";
820
        $sql .= " type_code=" . (isset($this->type_code) ? "'" . $this->db->escape($this->type_code) . "'" : "null") . ",";
821
        $sql .= " category_code=" . (isset($this->category_code) ? "'" . $this->db->escape($this->category_code) . "'" : "null") . ",";
822
        $sql .= " severity_code=" . (isset($this->severity_code) ? "'" . $this->db->escape($this->severity_code) . "'" : "null") . ",";
823
        $sql .= " datec=" . (dol_strlen($this->datec) != 0 ? "'" . $this->db->idate($this->datec) . "'" : 'null') . ",";
824
        $sql .= " date_read=" . (dol_strlen($this->date_read) != 0 ? "'" . $this->db->idate($this->date_read) . "'" : 'null') . ",";
825
        $sql .= " date_close=" . (dol_strlen($this->date_close) != 0 ? "'" . $this->db->idate($this->date_close) . "'" : 'null') . "";
826
        $sql .= " WHERE rowid=" . $this->id;
827
828
        $this->db->begin();
829
830
        $resql = $this->db->query($sql);
831
        if (!$resql) {
832
            $error++;
833
            $this->errors[] = "Error " . $this->db->lasterror();
834
        }
835
836
        if (! $error) {
837
            // Update extrafields
838
            if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) { // For avoid conflicts if trigger used
839
                $result = $this->insertExtraFields();
840
                if ($result < 0) {
841
                    $error++;
842
                }
843
            }
844
        }
845
846
        if (! $error && ! $notrigger) {
847
            // Call trigger
848
            $result=$this->call_trigger('TICKET_MODIFY', $user);
849
            if ($result < 0) {
850
                $error++;
851
            }
852
              // End call triggers
853
        }
854
855
        // Commit or rollback
856
        if ($error) {
857
            foreach ($this->errors as $errmsg) {
858
                dol_syslog(get_class($this) . "::update " . $errmsg, LOG_ERR);
859
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
860
            }
861
            $this->db->rollback();
862
            return -1 * $error;
863
        } else {
864
            $this->db->commit();
865
            return 1;
866
        }
867
    }
868
869
    /**
870
     *  Delete object in database
871
     *
872
     *     @param  User $user      User that deletes
873
     *  @param  int  $notrigger 0=launch triggers after, 1=disable triggers
874
     *  @return int                     <0 if KO, >0 if OK
875
     */
876
    public function delete($user, $notrigger = 0)
877
    {
878
        global $conf, $langs;
879
        $error = 0;
880
881
        $this->db->begin();
882
883
        if (!$error) {
884
            if (!$notrigger) {
885
                // Call trigger
886
                $result = $this->call_trigger('TICKET_DELETE', $user);
887
                if ($result < 0) {
888
                    $error++;
889
                }
890
                // End call triggers
891
            }
892
        }
893
894
        if (!$error) {
895
            // Delete linked contacts
896
            $res = $this->delete_linked_contact();
897
            if ($res < 0) {
898
                dol_syslog(get_class($this) . "::delete error", LOG_ERR);
899
                $error++;
900
            }
901
        }
902
903
        if (!$error) {
904
            // Delete linked object
905
            $res = $this->deleteObjectLinked();
906
            if ($res < 0) $error++;
907
        }
908
909
        // Removed extrafields
910
        if (!$error) {
911
            $result = $this->deleteExtraFields();
912
            if ($result < 0) {
913
                $error++;
914
                dol_syslog(get_class($this) . "::delete error -3 " . $this->error, LOG_ERR);
915
            }
916
        }
917
918
        if (!$error) {
919
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . "ticket";
920
            $sql .= " WHERE rowid=" . $this->id;
921
922
            dol_syslog(get_class($this) . "::delete sql=" . $sql);
923
            $resql = $this->db->query($sql);
924
            if (!$resql) {
925
                $error++;
926
                $this->errors[] = "Error " . $this->db->lasterror();
927
            }
928
        }
929
930
        // Commit or rollback
931
        if ($error) {
932
            foreach ($this->errors as $errmsg) {
933
                dol_syslog(get_class($this) . "::delete " . $errmsg, LOG_ERR);
934
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
935
            }
936
            $this->db->rollback();
937
            return -1 * $error;
938
        } else {
939
            $this->db->commit();
940
            return 1;
941
        }
942
    }
943
944
    /**
945
     *     Load an object from its id and create a new one in database
946
     *
947
     *     @param   User    $user       User that clone
948
     *     @param   int     $fromid     Id of object to clone
949
     *     @return  int                 New id of clone
950
     */
951
    public function createFromClone(User $user, $fromid)
952
    {
953
        $error = 0;
954
955
        $object = new Ticket($this->db);
956
957
        $this->db->begin();
958
959
        // Load source object
960
        $object->fetch($fromid);
961
        $object->id = 0;
962
        $object->statut = 0;
963
964
        // Clear fields
965
        // ...
966
        // Create clone
967
        $object->context['createfromclone'] = 'createfromclone';
968
        $result = $object->create($user);
969
970
        // Other options
971
        if ($result < 0) {
972
            $this->error = $object->error;
973
            $error++;
974
        }
975
976
        if (!$error) {
977
        }
978
979
        unset($object->context['createfromclone']);
980
981
        // End
982
        if (!$error) {
983
            $this->db->commit();
984
            return $object->id;
985
        } else {
986
            $this->db->rollback();
987
            return -1;
988
        }
989
    }
990
991
    /**
992
     *     Initialise object with example values
993
     *     Id must be 0 if object instance is a specimen
994
     *
995
     *     @return void
996
     */
997
    public function initAsSpecimen()
998
    {
999
        $this->id = 0;
1000
1001
        $this->ref = 'TI0501-001';
1002
        $this->track_id = 'XXXXaaaa';
1003
        $this->origin_email = '[email protected]';
1004
        $this->fk_project = '1';
1 ignored issue
show
Documentation Bug introduced by
The property $fk_project was declared of type integer, but '1' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1005
        $this->fk_user_create = '1';
1 ignored issue
show
Documentation Bug introduced by
The property $fk_user_create was declared of type integer, but '1' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1006
        $this->fk_user_assign = '1';
1 ignored issue
show
Documentation Bug introduced by
The property $fk_user_assign was declared of type integer, but '1' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1007
        $this->subject = 'Subject of ticket';
1008
        $this->message = 'Message of ticket';
1009
        $this->fk_statut = '0';
1 ignored issue
show
Documentation Bug introduced by
The property $fk_statut was declared of type integer, but '0' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1010
        $this->resolution = '1';
1011
        $this->progress = '10';
1 ignored issue
show
Documentation Bug introduced by
The property $progress was declared of type integer, but '10' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1012
        $this->timing = '30';
1 ignored issue
show
Documentation Bug introduced by
The property $timing was declared of type integer, but '30' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1013
        $this->type_code = 'TYPECODE';
1014
        $this->category_code = 'CATEGORYCODE';
1015
        $this->severity_code = 'SEVERITYCODE';
1016
        $this->datec = '';
1 ignored issue
show
Documentation Bug introduced by
The property $datec was declared of type integer, but '' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1017
        $this->date_read = '';
1 ignored issue
show
Documentation Bug introduced by
The property $date_read was declared of type integer, but '' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1018
        $this->date_close = '';
1 ignored issue
show
Documentation Bug introduced by
The property $date_close was declared of type integer, but '' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1019
        $this->tms = '';
1020
    }
1021
1022
    /**
1023
     * print selected status
1024
     *
1025
     * @param string    $selected   selected status
1026
     * @return void
1027
     */
1028
    public function printSelectStatus($selected = "")
1029
    {
1030
        print Form::selectarray('search_fk_statut', $this->statuts_short, $selected, $show_empty = 1, $key_in_label = 0, $value_as_key = 0, $option = '', $translate = 1, $maxlen = 0, $disabled = 0, $sort = '', $morecss = '');
1031
    }
1032
1033
1034
    /**
1035
     *      Charge dans cache la liste des types de tickets (paramétrable dans dictionnaire)
1036
     *
1037
     *      @return int             Number of lines loaded, 0 if already loaded, <0 if KO
1038
     */
1039
    public function loadCacheTypesTickets()
1040
    {
1041
        global $langs;
1042
1043
        if (! empty($this->cache_types_tickets) && count($this->cache_types_tickets)) {
1044
            return 0;
1045
        }
1046
        // Cache deja charge
1047
1048
        $sql = "SELECT rowid, code, label, use_default, pos, description";
1049
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_ticket_type";
1050
        $sql .= " WHERE active > 0";
1051
        $sql .= " ORDER BY pos";
1052
        dol_syslog(get_class($this) . "::load_cache_type_tickets sql=" . $sql, LOG_DEBUG);
1053
        $resql = $this->db->query($sql);
1054
        if ($resql) {
1055
            $num = $this->db->num_rows($resql);
1056
            $i = 0;
1057
            while ($i < $num) {
1058
                $obj = $this->db->fetch_object($resql);
1059
                // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
1060
                $label = ($langs->trans("TicketTypeShort" . $obj->code) != ("TicketTypeShort" . $obj->code) ? $langs->trans("TicketTypeShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
1061
                $this->cache_types_tickets[$obj->rowid]['code'] = $obj->code;
1062
                $this->cache_types_tickets[$obj->rowid]['label'] = $label;
1063
                $this->cache_types_tickets[$obj->rowid]['use_default'] = $obj->use_default;
1064
                $this->cache_types_tickets[$obj->rowid]['pos'] = $obj->pos;
1065
                $i++;
1066
            }
1067
            return $num;
1068
        } else {
1069
            dol_print_error($this->db);
1070
            return -1;
1071
        }
1072
    }
1073
1074
    /**
1075
     *      Charge dans cache la liste des catégories de tickets (paramétrable dans dictionnaire)
1076
     *
1077
     *      @return int             Number of lines loaded, 0 if already loaded, <0 if KO
1078
     */
1079
    public function loadCacheCategoriesTickets()
1080
    {
1081
        global $langs;
1082
1083
        if (! empty($this->cache_category_ticket) && count($this->cache_category_tickets)) {
0 ignored issues
show
Bug introduced by
The property cache_category_ticket does not exist on Ticket. Did you mean cache_category_tickets?
Loading history...
1084
            return 0;
1085
        }
1086
        // Cache deja charge
1087
1088
        $sql = "SELECT rowid, code, label, use_default, pos, description";
1089
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_ticket_category";
1090
        $sql .= " WHERE active > 0";
1091
        $sql .= " ORDER BY pos";
1092
        dol_syslog(get_class($this) . "::load_cache_categories_tickets sql=" . $sql, LOG_DEBUG);
1093
        $resql = $this->db->query($sql);
1094
        if ($resql) {
1095
            $num = $this->db->num_rows($resql);
1096
            $i = 0;
1097
            while ($i < $num) {
1098
                $obj = $this->db->fetch_object($resql);
1099
                $this->cache_category_tickets[$obj->rowid]['code'] = $obj->code;
1100
                // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
1101
                $label = ($langs->trans("TicketCategoryShort" . $obj->code) != ("TicketCategoryShort" . $obj->code) ? $langs->trans("TicketCategoryShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
1102
                $this->cache_category_tickets[$obj->rowid]['label'] = $label;
1103
                $this->cache_category_tickets[$obj->rowid]['use_default'] = $obj->use_default;
1104
                $this->cache_category_tickets[$obj->rowid]['pos'] = $obj->pos;
1105
                $i++;
1106
            }
1107
            return $num;
1108
        } else {
1109
            dol_print_error($this->db);
1110
            return -1;
1111
        }
1112
    }
1113
1114
    /**
1115
     *      Charge dans cache la liste des sévérité de tickets (paramétrable dans dictionnaire)
1116
     *
1117
     *      @return int             Number of lines loaded, 0 if already loaded, <0 if KO
1118
     */
1119
    public function loadCacheSeveritiesTickets()
1120
    {
1121
        global $langs;
1122
1123
        if (! empty($this->cache_severity_tickets) && count($this->cache_severity_tickets)) {
1124
            return 0;
1125
        }
1126
        // Cache deja charge
1127
1128
        $sql = "SELECT rowid, code, label, use_default, pos, description";
1129
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_ticket_severity";
1130
        $sql .= " WHERE active > 0";
1131
        $sql .= " ORDER BY pos";
1132
        dol_syslog(get_class($this) . "::loadCacheSeveritiesTickets sql=" . $sql, LOG_DEBUG);
1133
        $resql = $this->db->query($sql);
1134
        if ($resql) {
1135
            $num = $this->db->num_rows($resql);
1136
            $i = 0;
1137
            while ($i < $num) {
1138
                $obj = $this->db->fetch_object($resql);
1139
1140
                $this->cache_severity_tickets[$obj->rowid]['code'] = $obj->code;
1141
                // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
1142
                $label = ($langs->trans("TicketSeverityShort" . $obj->code) != ("TicketSeverityShort" . $obj->code) ? $langs->trans("TicketSeverityShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
1143
                $this->cache_severity_tickets[$obj->rowid]['label'] = $label;
1144
                $this->cache_severity_tickets[$obj->rowid]['use_default'] = $obj->use_default;
1145
                $this->cache_severity_tickets[$obj->rowid]['pos'] = $obj->pos;
1146
                $i++;
1147
            }
1148
            return $num;
1149
        } else {
1150
            dol_print_error($this->db);
1151
            return -1;
1152
        }
1153
    }
1154
1155
1156
    /**
1157
     * Return status label of object
1158
     *
1159
     * @param      int		$mode     0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
1160
     * @return     string    			  Label
1161
     */
1162
    public function getLibStatut($mode = 0)
1163
    {
1164
        return $this->libStatut($this->fk_statut, $mode);
1165
    }
1166
1167
1168
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1169
    /**
1170
     *    Return status label of object
1171
     *
1172
     *    @param      string 	$statut      id statut
1173
     *    @param      int		$mode        0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
1174
     *    @return     string     			 Label
1175
     */
1176
    public function LibStatut($statut, $mode = 0)
1177
    {
1178
        // phpcs:enable
1179
        global $langs;
1180
1181
        if ($mode == 0) {
1182
            return $langs->trans($this->statuts[$statut]);
1183
        }
1184
        elseif ($mode == 1) {
1185
            return $langs->trans($this->statuts_short[$statut]);
1186
        }
1187
        elseif ($mode == 2) {
1188
            if ($statut == self::STATUS_NOT_READ) {
1189
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut0.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]);
1190
            }
1191
            elseif ($statut == self::STATUS_READ) {
1192
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut1.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]);
1193
            }
1194
            elseif ($statut == self::STATUS_ASSIGNED) {
1195
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut2.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]);
1196
            }
1197
            elseif ($statut == self::STATUS_IN_PROGRESS) {
1198
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut3.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]);
1199
            }
1200
            elseif ($statut == self::STATUS_NEED_MORE_INFO) {
1201
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut5.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]);
1202
            }
1203
            elseif ($statut == self::STATUS_WAITING) {
1204
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut6.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]);
1205
            }
1206
            elseif ($statut == self::STATUS_CLOSED) {
1207
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut8.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]);
1208
            }
1209
            elseif ($statut == self::STATUS_CANCELED) {
1210
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut9.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]);
1211
            }
1212
        }
1213
        elseif ($mode == 3) {
1214
            if ($statut == self::STATUS_NOT_READ) {
1215
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut0.png@ticket');
1216
            }
1217
            elseif ($statut == self::STATUS_READ) {
1218
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut1.png@ticket');
1219
            }
1220
            elseif ($statut == self::STATUS_ASSIGNED) {
1221
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut2.png@ticket');
1222
            }
1223
            elseif ($statut == self::STATUS_IN_PROGRESS) {
1224
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut3.png@ticket');
1225
            }
1226
            elseif ($statut == self::STATUS_NEED_MORE_INFO) {
1227
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut5.png@ticket');
1228
            }
1229
            elseif ($statut == self::STATUS_WAITING) {
1230
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut6.png@ticket');
1231
            }
1232
            elseif ($statut == self::STATUS_CLOSED) {
1233
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut8.png@ticket');
1234
            }
1235
            elseif ($statut == self::STATUS_CANCELED) {
1236
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut9.png@ticket');
1237
            }
1238
        }
1239
        elseif ($mode == 4) {
1240
            if ($statut == self::STATUS_NOT_READ) {
1241
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut0.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]);
1242
            }
1243
            elseif ($statut == self::STATUS_READ) {
1244
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut1.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]);
1245
            }
1246
            elseif ($statut == self::STATUS_ASSIGNED) {
1247
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut2.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]);
1248
            }
1249
            elseif ($statut == self::STATUS_IN_PROGRESS) {
1250
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut3.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]);
1251
            }
1252
            elseif ($statut == self::STATUS_NEED_MORE_INFO) {
1253
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut5.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]);
1254
            }
1255
            elseif ($statut == self::STATUS_WAITING) {
1256
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut6.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]);
1257
            }
1258
            elseif ($statut == self::STATUS_CLOSED) {
1259
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut8.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]);
1260
            }
1261
            elseif ($statut == self::STATUS_CANCELED) {
1262
                return img_picto($langs->trans($this->statuts_short[$statut]), 'statut9.png@ticket') . ' ' . $langs->trans($this->statuts_short[$statut]);
1263
            }
1264
        }
1265
        elseif ($mode == 5 || $mode == 6) {
1266
            if ($statut == self::STATUS_NOT_READ) {
1267
                return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut0.png@ticket');
1268
            }
1269
            elseif ($statut == self::STATUS_READ) {
1270
                return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut1.png@ticket');
1271
            }
1272
            elseif ($statut == self::STATUS_ASSIGNED) {
1273
                return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut2.png@ticket');
1274
            }
1275
            elseif ($statut == self::STATUS_IN_PROGRESS) {
1276
                return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut3.png@ticket');
1277
            }
1278
            elseif ($statut == self::STATUS_NEED_MORE_INFO) {
1279
                return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut5.png@ticket');
1280
            }
1281
            elseif ($statut == self::STATUS_WAITING) {
1282
                return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut6.png@ticket');
1283
            }
1284
            elseif ($statut == self::STATUS_CLOSED) {
1285
                return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut8.png@ticket');
1286
            }
1287
            elseif ($statut == self::STATUS_CANCELED) {
1288
                return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut9.png@ticket');
1289
            }
1290
        }
1291
    }
1292
1293
1294
    /**
1295
     *  Return a link to the object card (with optionaly the picto)
1296
     *
1297
     *	@param	int		$withpicto					Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto)
1298
     *	@param	string	$option						On what the link point to ('nolink', ...)
1299
     *  @param	int  	$notooltip					1=Disable tooltip
1300
     *  @param  string  $morecss            		Add more css on link
1301
     *  @param  int     $save_lastsearch_value    	-1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
1302
     *	@return	string								String with URL
1303
     */
1304
    public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1305
    {
1306
        global $db, $conf, $langs;
1307
        global $dolibarr_main_authentication, $dolibarr_main_demo;
1308
        global $menumanager;
1309
1310
        if (! empty($conf->dol_no_mouse_hover)) $notooltip=1;   // Force disable tooltips
1311
1312
        $result = '';
1313
        $companylink = '';
1314
1315
        $label = '<u>' . $langs->trans("ShowTicket") . '</u>';
1316
        $label.= '<br>';
1317
        $label.= '<b>' . $langs->trans('Ref') . ':</b> ' . $this->ref.'<br>';
1318
        $label.= '<b>' . $langs->trans('TicketTrackId') . ':</b> ' . $this->track_id.'<br>';
1319
        $label.= '<b>' . $langs->trans('Subject') . ':</b> ' . $this->subject;
1320
1321
        $url = dol_buildpath('/ticket/card.php', 1).'?id='.$this->id;
1322
1323
        if ($option != 'nolink')
1324
        {
1325
            // Add param to save lastsearch_values or not
1326
            $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
1327
            if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
1328
            if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
1329
        }
1330
1331
        $linkclose='';
1332
        if (empty($notooltip))
1333
        {
1334
            if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
1335
            {
1336
                $label=$langs->trans("ShowTicket");
1337
                $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
1338
            }
1339
            $linkclose.=' title="'.dol_escape_htmltag($label, 1).'"';
1340
            $linkclose.=' class="classfortooltip'.($morecss?' '.$morecss:'').'"';
1341
        }
1342
        else $linkclose = ($morecss?' class="'.$morecss.'"':'');
1343
1344
        $linkstart = '<a href="'.$url.'"';
1345
        $linkstart.=$linkclose.'>';
1346
        $linkend='</a>';
1347
1348
        $result .= $linkstart;
1349
        if ($withpicto) $result.=img_object(($notooltip?'':$label), ($this->picto?$this->picto:'generic'), ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
1350
        if ($withpicto != 2) $result.= $this->ref;
1351
        $result .= $linkend;
1352
        //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1353
1354
        return $result;
1355
    }
1356
1357
1358
    /**
1359
     *    Mark a message as read
1360
     *
1361
     *    @param    User		$user			    Object user
1362
     *    @param	  int			$notrigger		No trigger
1363
     *    @return   int							      <0 if KO, >0 if OK
1364
     */
1365
    public function markAsRead($user, $notrigger = 0)
1366
    {
1367
        global $conf, $langs;
1368
1369
        if ($this->statut != self::STATUS_CANCELED) { // no closed
1370
            $this->db->begin();
1371
1372
            $sql = "UPDATE " . MAIN_DB_PREFIX . "ticket";
1373
            $sql .= " SET fk_statut = ".Ticket::STATUS_READ.", date_read='" . $this->db->idate(dol_now()) . "'";
1374
            $sql .= " WHERE rowid = " . $this->id;
1375
1376
            dol_syslog(get_class($this) . "::markAsRead");
1377
            $resql = $this->db->query($sql);
1378
            if ($resql) {
1379
                $this->actionmsg = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));
1380
                $this->actionmsg2 = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));
1381
1382
                if (!$error && !$notrigger) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $error seems to be never defined.
Loading history...
1383
                    // Call trigger
1384
                    $result=$this->call_trigger('TICKET_MODIFY', $user);
1385
                    if ($result < 0) {
1386
                        $error++;
1387
                    }
1388
                    // End call triggers
1389
                }
1390
1391
                if (!$error) {
1392
                    $this->db->commit();
1393
                    return 1;
1394
                } else {
1395
                    $this->db->rollback();
1396
                    $this->error = join(',', $this->errors);
1397
                    dol_syslog(get_class($this) . "::markAsRead " . $this->error, LOG_ERR);
1398
                    return -1;
1399
                }
1400
            } else {
1401
                $this->db->rollback();
1402
                $this->error = $this->db->lasterror();
1403
                dol_syslog(get_class($this) . "::markAsRead " . $this->error, LOG_ERR);
1404
                return -1;
1405
            }
1406
        }
1407
    }
1408
1409
    /**
1410
     *    Mark a message as read
1411
     *
1412
     *    @param    User	$user				Object user
1413
     *    @param    int 	$id_assign_user		ID of user assigned
1414
     *    @param    int 	$notrigger        	Disable trigger
1415
     *    @return   int							<0 if KO, 0=Nothing done, >0 if OK
1416
     */
1417
    public function assignUser($user, $id_assign_user, $notrigger = 0)
1418
    {
1419
        global $conf, $langs;
1420
1421
        $this->db->begin();
1422
1423
        $this->oldcopy = dol_clone($this);
1424
1425
        $sql = "UPDATE " . MAIN_DB_PREFIX . "ticket";
1426
        if ($id_assign_user > 0)
1427
        {
1428
            $sql .= " SET fk_user_assign=".$id_assign_user.", fk_statut = ".Ticket::STATUS_ASSIGNED;
1429
        }
1430
        else
1431
        {
1432
            $sql .= " SET fk_user_assign=null, fk_statut = ".Ticket::STATUS_READ;
1433
        }
1434
        $sql .= " WHERE rowid = " . $this->id;
1435
1436
        dol_syslog(get_class($this) . "::assignUser sql=" . $sql);
1437
        $resql = $this->db->query($sql);
1438
        if ($resql)
1439
        {
1440
            $this->fk_user_assign = $id_assign_user; // May be used by trigger
1441
1442
            if (! $notrigger) {
1443
                // Call trigger
1444
                $result = $this->call_trigger('TICKET_ASSIGNED', $user);
1445
                if ($result < 0) {
1446
                    $error ++;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $error seems to be never defined.
Loading history...
1447
                }
1448
                // End call triggers
1449
            }
1450
1451
            if (! $error) {
1452
                $this->db->commit();
1453
                return 1;
1454
            } else {
1455
                $this->db->rollback();
1456
                $this->error = join(',', $this->errors);
1457
                dol_syslog(get_class($this) . "::assignUser " . $this->error, LOG_ERR);
1458
                return - 1;
1459
            }
1460
        } else {
1461
            $this->db->rollback();
1462
            $this->error = $this->db->lasterror();
1463
            dol_syslog(get_class($this) . "::assignUser " . $this->error, LOG_ERR);
1464
            return - 1;
1465
        }
1466
    }
1467
1468
1469
    /**
1470
     *  Send notification of changes by email
1471
     *
1472
     * 	@param  User   $user    		User that create
1473
     * 	@param  string $message 		Log message
1474
     * 	@return int                 	<0 if KO, >0 if OK (number of emails sent)
1475
     */
1476
    private function sendLogByEmail($user, $message)
1477
    {
1478
        global $conf, $langs;
1479
1480
        $nb_sent = 0;
1481
1482
        $langs->load('ticket');
1483
1484
        // Retrieve email of all contacts (internal and external)
1485
        $contacts = $this->listeContact(-1, 'internal');
1486
        $contacts = array_merge($contacts, $this->listeContact(-1, 'external'));
1487
1488
        /* If origin_email and no socid, we add email to the list * */
1489
        if (!empty($this->origin_email) && empty($this->fk_soc)) {
1490
            $array_ext = array(array('firstname' => '', 'lastname' => '', 'email' => $this->origin_email, 'libelle' => $langs->transnoentities('TicketEmailOriginIssuer'), 'socid' => "-1"));
1491
            $contacts = array_merge($contacts, $array_ext);
1492
        }
1493
1494
        if (!empty($this->fk_soc)) {
1495
            $this->fetch_thirdparty($this->fk_soc);
1496
            $array_company = array(array('firstname' => '', 'lastname' => $this->client->name, 'email' => $this->client->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $this->client->id));
0 ignored issues
show
Bug Best Practice introduced by
The property client does not exist on Ticket. Did you maybe forget to declare it?
Loading history...
1497
            $contacts = array_merge($contacts, $array_company);
1498
        }
1499
1500
        // foreach contact send email with notification message
1501
        if (count($contacts) > 0) {
1502
            foreach ($contacts as $key => $info_sendto) {
1503
                $message = '';
1504
                $subject = '[' . $conf->global->MAIN_INFO_SOCIETE_NOM . '] ' . $langs->transnoentities('TicketNotificationEmailSubject', $this->track_id);
1505
                $message .= $langs->transnoentities('TicketNotificationEmailBody', $this->track_id) . "\n\n";
1506
                $message .= $langs->transnoentities('Title') . ' : ' . $this->subject . "\n";
1507
1508
                $recipient_name = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1');
1509
                $recipient = (!empty($recipient_name) ? $recipient_name : $info_sendto['email']) . ' (' . strtolower($info_sendto['libelle']) . ')';
1510
                $message .= $langs->transnoentities('TicketNotificationRecipient') . ' : ' . $recipient . "\n";
1511
                $message .= "\n";
1512
                $message .= '* ' . $langs->transnoentities('TicketNotificationLogMessage') . ' *' . "\n";
1513
                $message .= dol_html_entity_decode($log_message, ENT_QUOTES) . "\n";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $log_message does not exist. Did you maybe mean $message?
Loading history...
1514
1515
                if ($info_sendto['source'] == 'internal') {
1516
                    $url_internal_ticket = dol_buildpath('/ticket/card.php', 2) . '?track_id=' . $this->track_id;
1517
                    $message .= "\n" . $langs->transnoentities('TicketNotificationEmailBodyInfosTrackUrlinternal') . ' : ' . '<a href="' . $url_internal_ticket . '">' . $this->track_id . '</a>' . "\n";
1518
                } else {
1519
                    $url_public_ticket = ($conf->global->TICKET_URL_PUBLIC_INTERFACE ? $conf->global->TICKET_URL_PUBLIC_INTERFACE . '/' : dol_buildpath('/public/ticket/view.php', 2)) . '?track_id=' . $this->track_id;
1520
                    $message .= "\n" . $langs->transnoentities('TicketNewEmailBodyInfosTrackUrlCustomer') . ' : ' . '<a href="' . $url_public_ticket . '">' . $this->track_id . '</a>' . "\n";
1521
                }
1522
1523
                $message .= "\n";
1524
                $message .= $langs->transnoentities('TicketEmailPleaseDoNotReplyToThisEmail') . "\n";
1525
1526
                $from = $conf->global->MAIN_INFO_SOCIETE_NOM . '<' . $conf->global->TICKET_NOTIFICATION_EMAIL_FROM . '>';
1527
                $replyto = $from;
1528
1529
                // Init to avoid errors
1530
                $filepath = array();
1531
                $filename = array();
1532
                $mimetype = array();
1533
1534
                $message = dol_nl2br($message);
1535
1536
                if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) {
1537
                    $old_MAIN_MAIL_AUTOCOPY_TO = $conf->global->MAIN_MAIL_AUTOCOPY_TO;
1538
                    $conf->global->MAIN_MAIL_AUTOCOPY_TO = '';
1539
                }
1540
                include_once DOL_DOCUMENT_ROOT . '/core/class/CMailFile.class.php';
1541
                $mailfile = new CMailFile($subject, $info_sendto['email'], $from, $message, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, 0);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $deliveryreceipt seems to be never defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable $sendtocc seems to be never defined.
Loading history...
1542
                if ($mailfile->error || $mailfile->errors) {
1543
                    setEventMessages($mailfile->error, $mailfile->errors, 'errors');
1544
                } else {
1545
                    $result = $mailfile->sendfile();
1546
                    if ($result > 0) {
1547
                        $nb_sent++;
1548
                    }
1549
                }
1550
                if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) {
1551
                    $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $old_MAIN_MAIL_AUTOCOPY_TO does not seem to be defined for all execution paths leading up to this point.
Loading history...
1552
                }
1553
            }
1554
1555
            setEventMessages($langs->trans('TicketNotificationNumberEmailSent', $nb_sent), null, 'mesgs');
1556
        }
1557
1558
        return $nb_sent;
1559
    }
1560
1561
    /**
1562
     *      Charge la liste des actions sur le ticket
1563
     *
1564
     *      @return int             Number of lines loaded, 0 if already loaded, <0 if KO
1565
     */
1566
    public function loadCacheLogsTicket()
1567
    {
1568
        global $langs;
1569
1570
        if (is_array($this->cache_logs_ticket) && count($this->cache_logs_ticket)) {
0 ignored issues
show
Bug introduced by
The property cache_logs_ticket does not exist on Ticket. Did you mean cache_msgs_ticket?
Loading history...
1571
            return 0;
1572
        }
1573
        // Cache deja charge
1574
1575
        // TODO Read the table llx_actioncomm
1576
        /*
1577
        $sql = "SELECT rowid, fk_user_create, datec, message";
1578
        $sql .= " FROM " . MAIN_DB_PREFIX . "ticket_logs";
1579
        $sql .= " WHERE fk_track_id ='" . $this->db->escape($this->track_id) . "'";
1580
        $sql .= " ORDER BY datec DESC";
1581
1582
        $resql = $this->db->query($sql);
1583
        if ($resql) {
1584
            $num = $this->db->num_rows($resql);
1585
            $i = 0;
1586
            while ($i < $num) {
1587
                $obj = $this->db->fetch_object($resql);
1588
                $this->cache_logs_ticket[$i]['id'] = $obj->rowid;
1589
                $this->cache_logs_ticket[$i]['fk_user_create'] = $obj->fk_user_create;
1590
                $this->cache_logs_ticket[$i]['datec'] = $this->db->jdate($obj->datec);
1591
                $this->cache_logs_ticket[$i]['message'] = $obj->message;
1592
                $i++;
1593
            }
1594
            return $num;
1595
        } else {
1596
            $this->error = "Error " . $this->db->lasterror();
1597
            dol_syslog(get_class($this) . "::loadCacheLogsTicket " . $this->error, LOG_ERR);
1598
            return -1;
1599
        }*/
1600
1601
        return 0;
1602
    }
1603
1604
    /**
1605
     *  Add message into database
1606
     *
1607
     *  @param  User $user      	User that creates
1608
     *  @param  int  $notrigger 	0=launch triggers after, 1=disable triggers
1609
     *  @return int                 <0 if KO, Id of created object if OK
1610
     */
1611
    public function createTicketMessage($user, $notrigger = 0)
1612
    {
1613
        global $conf, $langs;
1614
        $error = 0;
1615
1616
        $now = dol_now();
1617
1618
        // Clean parameters
1619
        if (isset($this->fk_track_id)) {
1620
            $this->fk_track_id = trim($this->fk_track_id);
1621
        }
1622
1623
        if (isset($this->message)) {
1624
            $this->message = trim($this->message);
1625
        }
1626
1627
        $this->db->begin();
1628
1629
        // Insert entry into agenda with code 'TICKET_MSG'
1630
        include_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
1631
        $actioncomm=new ActionComm($this->db);
1632
        $actioncomm->type_code = 'AC_OTH_AUTO';
1633
        $actioncomm->code = 'TICKET_MSG';
1634
        $actioncomm->socid = $this->socid;
1635
        $actioncomm->label = $this->subject;
1636
        $actioncomm->note = $this->message;
1 ignored issue
show
Deprecated Code introduced by
The property CommonObject::$note has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1636
        /** @scrutinizer ignore-deprecated */ $actioncomm->note = $this->message;
Loading history...
1637
        $actioncomm->userassigned = array($user->id);
1638
        $actioncomm->userownerid = $user->id;
1639
        $actioncomm->datep = $now;
1640
        $actioncomm->percentage = 100;
1641
        $actioncomm->elementtype = 'ticket';
1642
        $actioncomm->fk_element = $this->id;
1643
1644
        $actionid = $actioncomm->create($user);
1645
        if ($actionid <= 0)
1646
        {
1647
            $error++;
1648
            $this->error = $actioncomm->error;
1649
            $this->errors = $actioncomm->errors;
1650
        }
1651
1652
        // Commit or rollback
1653
        if ($error) {
1654
            $this->db->rollback();
1655
            return -1 * $error;
1656
        } else {
1657
            $this->db->commit();
1658
            return 1;
1659
        }
1660
    }
1661
1662
    /**
1663
     *      Load the list of event on ticket into ->cache_msgs_ticket
1664
     *
1665
     *      @return int             Number of lines loaded, 0 if already loaded, <0 if KO
1666
     */
1667
    public function loadCacheMsgsTicket()
1668
    {
1669
        if (is_array($this->cache_msgs_ticket) && count($this->cache_msgs_ticket)) {
1670
            return 0;
1671
        }
1672
1673
        // Cache already loaded
1674
1675
        $sql = "SELECT id as rowid, fk_user_author, datec, label, note as message, visibility";
1676
        $sql .= " FROM " . MAIN_DB_PREFIX . "actioncomm";
1677
        $sql .= " WHERE fk_element = " . (int) $this->id;
1678
        $sql .= " AND elementtype = 'ticket'";
1679
        $sql .= " ORDER BY datec DESC";
1680
1681
        dol_syslog(get_class($this) . "::load_cache_actions_ticket sql=" . $sql, LOG_DEBUG);
1682
        $resql = $this->db->query($sql);
1683
        if ($resql) {
1684
            $num = $this->db->num_rows($resql);
1685
            $i = 0;
1686
            while ($i < $num) {
1687
                $obj = $this->db->fetch_object($resql);
1688
                $this->cache_msgs_ticket[$i]['id'] = $obj->rowid;
1689
                $this->cache_msgs_ticket[$i]['fk_user_author'] = $obj->fk_user_author;
1690
                $this->cache_msgs_ticket[$i]['datec'] = $this->db->jdate($obj->datec);
1691
                $this->cache_msgs_ticket[$i]['subject'] = $obj->label;
1692
                $this->cache_msgs_ticket[$i]['message'] = $obj->message;
1693
                $this->cache_msgs_ticket[$i]['private'] = ($obj->visibility == 'private' ? 1 : 0);
1694
                $i++;
1695
            }
1696
            return $num;
1697
        } else {
1698
            $this->error = "Error " . $this->db->lasterror();
1699
            dol_syslog(get_class($this) . "::load_cache_actions_ticket " . $this->error, LOG_ERR);
1700
            return -1;
1701
        }
1702
    }
1703
1704
    /**
1705
     *    Close a ticket
1706
     *
1707
     *    @param    User    $user      User that close
1708
     *    @return   int		           <0 if KO, >0 if OK
1709
     */
1710
    public function close(User $user)
1711
    {
1712
        global $conf, $langs;
1713
1714
        if ($this->fk_statut != Ticket::STATUS_CLOSED) { // not closed
1715
            $this->db->begin();
1716
1717
            $sql = "UPDATE " . MAIN_DB_PREFIX . "ticket";
1718
            $sql .= " SET fk_statut=".Ticket::STATUS_CLOSED.", progress=100, date_close='" . $this->db->idate(dol_now()) . "'";
1719
            $sql .= " WHERE rowid = " . $this->id;
1720
1721
            dol_syslog(get_class($this) . "::close sql=" . $sql);
1722
            $resql = $this->db->query($sql);
1723
            if ($resql) {
1724
                $error = 0;
1725
1726
                // Valid and close fichinter linked
1727
                $this->fetchObjectLinked($this->id, $this->element, null, 'fichinter');
1728
                if ($this->linkedObjectsIds)
1729
                {
1730
                    foreach ($this->linkedObjectsIds['fichinter'] as $fichinter_id) {
1731
                        $fichinter = new Fichinter($this->db);
1732
                        $fichinter->fetch($fichinter_id);
1733
                        if ($fichinter->statut == 0) {
1734
                            $result = $fichinter->setValid($user);
1735
                            if (!$result) {
1736
                                $this->errors[] = $fichinter->error;
1737
                                $error++;
1738
                            }
1739
                        }
1740
                        if ($fichinter->statut < 3) {
1741
                            $result = $fichinter->setStatut(3);
1742
                            if (!$result) {
1743
                                $this->errors[] = $fichinter->error;
1744
                                $error++;
1745
                            }
1746
                        }
1747
                    }
1748
                }
1749
1750
                // Call trigger
1751
                $result=$this->call_trigger('TICKET_CLOSE', $user);
1752
                if ($result < 0) {
1753
                    $error++;
1754
                }
1755
                // End call triggers
1756
1757
                if (!$error) {
1758
                    $this->db->commit();
1759
                    return 1;
1760
                } else {
1761
                    $this->db->rollback();
1762
                    $this->error = join(',', $this->errors);
1763
                    dol_syslog(get_class($this) . "::close " . $this->error, LOG_ERR);
1764
                    return -1;
1765
                }
1766
            } else {
1767
                $this->db->rollback();
1768
                $this->error = $this->db->lasterror();
1769
                dol_syslog(get_class($this) . "::close " . $this->error, LOG_ERR);
1770
                return -1;
1771
            }
1772
        }
1773
    }
1774
1775
    /**
1776
     *     Search and fetch thirparties by email
1777
     *
1778
     *     @param  string $email   		Email
1779
     *     @param  int    $type    		Type of thirdparties (0=any, 1=customer, 2=prospect, 3=supplier)
1780
     *     @param  array  $filters 		Array of couple field name/value to filter the companies with the same name
1781
     *     @param  string $clause  		Clause for filters
1782
     *     @return array        		Array of thirdparties object
1783
     */
1784
    public function searchSocidByEmail($email, $type = '0', $filters = array(), $clause = 'AND')
1785
    {
1786
        $thirdparties = array();
1787
1788
        // Generation requete recherche
1789
        $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "societe";
1790
        $sql .= " WHERE entity IN (" . getEntity('ticket', 1) . ")";
1791
        if (!empty($type)) {
1792
            if ($type == 1 || $type == 2) {
1793
                $sql .= " AND client = " . $type;
1794
            } elseif ($type == 3) {
1795
                $sql .= " AND fournisseur = 1";
1796
            }
1797
        }
1798
        if (!empty($email)) {
1799
            if (!$exact) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $exact seems to be never defined.
Loading history...
1800
                if (preg_match('/^([\*])?[^*]+([\*])?$/', $email, $regs) && count($regs) > 1) {
1801
                    $email = str_replace('*', '%', $email);
1802
                } else {
1803
                    $email = '%' . $email . '%';
1804
                }
1805
            }
1806
            $sql .= " AND ";
1807
            if (is_array($filters) && !empty($filters)) {
1808
                $sql .= "(";
1809
            }
1810
1811
            if (!$case) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $case seems to be never defined.
Loading history...
1812
                $sql .= "email LIKE '" . $this->db->escape($email) . "'";
1813
            } else {
1814
                $sql .= "email LIKE BINARY '" . $this->db->escape($email) . "'";
1815
            }
1816
        }
1817
        if (is_array($filters) && !empty($filters)) {
1818
            foreach ($filters as $field => $value) {
1819
                $sql .= " " . $clause . " " . $field . " LIKE BINARY '" . $this->db->escape($value) . "'";
1820
            }
1821
            if (!empty($email)) {
1822
                $sql .= ")";
1823
            }
1824
        }
1825
1826
        $res = $this->db->query($sql);
1827
        if ($res) {
1828
            while ($rec = $this->db->fetch_array($res)) {
1829
                $soc = new Societe($this->db);
1830
                $soc->fetch($rec['rowid']);
1831
                $thirdparties[] = $soc;
1832
            }
1833
1834
            return $thirdparties;
1835
        } else {
1836
            $this->error = $this->db->error() . ' sql=' . $sql;
1837
            dol_syslog(get_class($this) . "::searchSocidByEmail " . $this->error, LOG_ERR);
1838
            return -1;
1839
        }
1840
    }
1841
1842
    /**
1843
     *     Search and fetch contacts by email
1844
     *
1845
     *     @param  string $email 	Email
1846
     *     @param  array  $socid 	Limit to a thirdparty
1847
     *     @param  string $case  	Respect case
1848
     *     @return array        	Array of contacts object
1849
     */
1850
    public function searchContactByEmail($email, $socid = '', $case = '')
1851
    {
1852
        $contacts = array();
1853
1854
        // Generation requete recherche
1855
        $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "socpeople";
1856
        $sql .= " WHERE entity IN (" . getEntity('socpeople') . ")";
1857
        if (!empty($socid)) {
1858
            $sql .= " AND fk_soc='" . $this->db->escape($socid) . "'";
1859
        }
1860
1861
        if (!empty($email)) {
1862
            $sql .= " AND ";
1863
1864
            if (!$case) {
1865
                $sql .= "email LIKE '" . $this->db->escape($email) . "'";
1866
            } else {
1867
                $sql .= "email LIKE BINARY '" . $this->db->escape($email) . "'";
1868
            }
1869
        }
1870
1871
        $res = $this->db->query($sql);
1872
        if ($res) {
1873
            while ($rec = $this->db->fetch_array($res)) {
1874
                include_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
1875
                $contactstatic = new Contact($this->db);
1876
                $contactstatic->fetch($rec['rowid']);
1877
                $contacts[] = $contactstatic;
1878
            }
1879
1880
            return $contacts;
1881
        } else {
1882
            $this->error = $this->db->error() . ' sql=' . $sql;
1883
            dol_syslog(get_class($this) . "::searchContactByEmail " . $this->error, LOG_ERR);
1884
            return -1;
1885
        }
1886
    }
1887
1888
    /**
1889
     *    Define parent commany of current ticket
1890
     *
1891
     *    @param  int $id Id of thirdparty to set or '' to remove
1892
     *    @return int             <0 if KO, >0 if OK
1893
     */
1894
    public function setCustomer($id)
1895
    {
1896
        if ($this->id) {
1897
            $sql = "UPDATE " . MAIN_DB_PREFIX . "ticket";
1898
            $sql .= " SET fk_soc = " . ($id > 0 ? $id : "null");
1899
            $sql .= " WHERE rowid = " . $this->id;
1900
            dol_syslog(get_class($this) . '::setCustomer sql=' . $sql);
1901
            $resql = $this->db->query($sql);
1902
            if ($resql) {
1903
                return 1;
1904
            } else {
1905
                return -1;
1906
            }
1907
        } else {
1908
            return -1;
1909
        }
1910
    }
1911
1912
    /**
1913
     *    Define progression of current ticket
1914
     *
1915
     *    @param  int $percent Progression percent
1916
     *    @return int             <0 if KO, >0 if OK
1917
     */
1918
    public function setProgression($percent)
1919
    {
1920
        if ($this->id) {
1921
            $sql = "UPDATE " . MAIN_DB_PREFIX . "ticket";
1922
            $sql .= " SET progress = " . ($percent > 0 ? $percent : "null");
1923
            $sql .= " WHERE rowid = " . $this->id;
1924
            dol_syslog(get_class($this) . '::set_progression sql=' . $sql);
1925
            $resql = $this->db->query($sql);
1926
            if ($resql) {
1927
                return 1;
1928
            } else {
1929
                return -1;
1930
            }
1931
        } else {
1932
            return -1;
1933
        }
1934
    }
1935
1936
    /**
1937
     *     Link element with a project
1938
     * 	   Override core function because of key name 'fk_project' used for this module
1939
     *
1940
     *     @param  int $projectid Project id to link element to
1941
     *     @return int                        <0 if KO, >0 if OK
1942
     */
1943
    public function setProject($projectid)
1944
    {
1945
        if (!$this->table_element) {
1946
            dol_syslog(get_class($this) . "::setProject was called on objet with property table_element not defined", LOG_ERR);
1947
            return -1;
1948
        }
1949
1950
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element;
1951
        if ($projectid) {
1952
            $sql .= ' SET fk_project = ' . $projectid;
1953
        } else {
1954
            $sql .= ' SET fk_project = NULL';
1955
        }
1956
1957
        $sql .= ' WHERE rowid = ' . $this->id;
1958
1959
        dol_syslog(get_class($this) . "::setProject sql=" . $sql);
1960
        if ($this->db->query($sql)) {
1961
            $this->fk_project = $projectid;
1962
            return 1;
1963
        } else {
1964
            dol_print_error($this->db);
1965
            return -1;
1966
        }
1967
    }
1968
1969
    /**
1970
     *     Link element with a contract
1971
     *
1972
     *     @param  int $contractid Contract id to link element to
1973
     *     @return int                        <0 if KO, >0 if OK
1974
     */
1975
    public function setContract($contractid)
1976
    {
1977
        if (!$this->table_element) {
1978
            dol_syslog(get_class($this) . "::setContract was called on objet with property table_element not defined", LOG_ERR);
1979
            return -1;
1980
        }
1981
1982
        $result = $this->add_object_linked('contrat', $contractid);
1983
        if ($result) {
1984
            $this->fk_contract = $contractid;
1985
            return 1;
1986
        } else {
1987
            dol_print_error($this->db);
1988
            return -1;
1989
        }
1990
    }
1991
1992
    /* gestion des contacts d'un ticket */
1993
1994
    /**
1995
     *  Return id des contacts interne de suivi
1996
     *
1997
     *  @return array       Liste des id contacts suivi ticket
1998
     */
1999
    public function getIdTicketInternalContact()
2000
    {
2001
        return $this->getIdContact('internal', 'SUPPORTTEC');
2002
    }
2003
2004
    /**
2005
     * Retrieve informations about internal contacts
2006
     *
2007
     *  @return array       Array with datas : firstname, lastname, socid (-1 for internal users), email, code, libelle, status
2008
     */
2009
    public function getInfosTicketInternalContact()
2010
    {
2011
        return $this->listeContact(-1, 'internal');
2012
    }
2013
2014
    /**
2015
     *  Return id des contacts clients pour le suivi ticket
2016
     *
2017
     *  @return array       Liste des id contacts suivi ticket
2018
     */
2019
    public function getIdTicketCustomerContact()
2020
    {
2021
        return $this->getIdContact('external', 'SUPPORTCLI');
2022
    }
2023
2024
    /**
2025
     * Retrieve informations about external contacts
2026
     *
2027
     *  @return array       Array with datas : firstname, lastname, socid (-1 for internal users), email, code, libelle, status
2028
     */
2029
    public function getInfosTicketExternalContact()
2030
    {
2031
        return $this->listeContact(-1, 'external');
2032
    }
2033
2034
    /**
2035
     *  Return id des contacts clients des intervenants
2036
     *
2037
     *  @return array       Liste des id contacts intervenants
2038
     */
2039
    public function getIdTicketInternalInvolvedContact()
2040
    {
2041
        return $this->getIdContact('internal', 'CONTRIBUTOR');
2042
    }
2043
2044
    /**
2045
     *  Return id des contacts clients des intervenants
2046
     *
2047
     *  @return array       Liste des id contacts intervenants
2048
     */
2049
    public function getIdTicketCustomerInvolvedContact()
2050
    {
2051
        return $this->getIdContact('external', 'CONTRIBUTOR');
2052
    }
2053
2054
    /**
2055
     * Return id of all contacts for ticket
2056
     *
2057
     * @return	array		Array of contacts for tickets
2058
     */
2059
    public function getTicketAllContacts()
2060
    {
2061
        $array_contact = array();
2062
2063
        $array_contact = $this->getIdTicketInternalContact($exclude_self);
1 ignored issue
show
Comprehensibility Best Practice introduced by
The variable $exclude_self seems to be never defined.
Loading history...
Unused Code introduced by
The call to Ticket::getIdTicketInternalContact() has too many arguments starting with $exclude_self. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2063
        /** @scrutinizer ignore-call */ 
2064
        $array_contact = $this->getIdTicketInternalContact($exclude_self);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
2064
2065
        $array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact($exclude_self));
1 ignored issue
show
Unused Code introduced by
The call to Ticket::getIdTicketCustomerContact() has too many arguments starting with $exclude_self. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2065
        $array_contact = array_merge($array_contact, $this->/** @scrutinizer ignore-call */ getIdTicketCustomerContact($exclude_self));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
2066
2067
        $array_contact = array_merge($array_contact, $this->getIdTicketInternalInvolvedContact($exclude_self));
1 ignored issue
show
Unused Code introduced by
The call to Ticket::getIdTicketInternalInvolvedContact() has too many arguments starting with $exclude_self. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2067
        $array_contact = array_merge($array_contact, $this->/** @scrutinizer ignore-call */ getIdTicketInternalInvolvedContact($exclude_self));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
2068
        $array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact($exclude_self));
1 ignored issue
show
Unused Code introduced by
The call to Ticket::getIdTicketCustomerInvolvedContact() has too many arguments starting with $exclude_self. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2068
        $array_contact = array_merge($array_contact, $this->/** @scrutinizer ignore-call */ getIdTicketCustomerInvolvedContact($exclude_self));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
2069
2070
        return $array_contact;
2071
    }
2072
2073
    /**
2074
     * Return id of all contacts for ticket
2075
     *
2076
     * @return	array		Array of contacts
2077
     */
2078
    public function getTicketAllCustomerContacts()
2079
    {
2080
        $array_contact = array();
2081
2082
        $array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact($exclude_self));
1 ignored issue
show
Unused Code introduced by
The call to Ticket::getIdTicketCustomerContact() has too many arguments starting with $exclude_self. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2082
        $array_contact = array_merge($array_contact, $this->/** @scrutinizer ignore-call */ getIdTicketCustomerContact($exclude_self));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Comprehensibility Best Practice introduced by
The variable $exclude_self seems to be never defined.
Loading history...
2083
        $array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact($exclude_self));
1 ignored issue
show
Unused Code introduced by
The call to Ticket::getIdTicketCustomerInvolvedContact() has too many arguments starting with $exclude_self. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2083
        $array_contact = array_merge($array_contact, $this->/** @scrutinizer ignore-call */ getIdTicketCustomerInvolvedContact($exclude_self));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
2084
2085
        return $array_contact;
2086
    }
2087
2088
    /**
2089
     * Send message
2090
     *
2091
     *  @param  string $subject	  Subject
2092
     *  @param  string $texte      Message to send
2093
     *  @return int                <0 if KO, or number of changes if OK
2094
     */
2095
    public function messageSend($subject, $texte)
2096
    {
2097
        global $conf, $langs, $mysoc, $dolibarr_main_url_root;
2098
2099
        $langs->load("other");
2100
2101
        dol_syslog(get_class($this) . "::message_send action=$action, socid=$socid, texte=$texte, objet_type=$objet_type, objet_id=$objet_id, file=$file");
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $socid seems to be never defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable $action does not exist. Did you maybe mean $actiondefid?
Loading history...
Comprehensibility Best Practice introduced by
The variable $file seems to be never defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable $objet_id seems to be never defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable $objet_type seems to be never defined.
Loading history...
2102
2103
        $internal_contacts = $this->getIdContact('internal', 'SUPPORTTEC');
2104
        $external_contacts = $this->getIdContact('external', 'SUPPORTTEC');
2105
2106
        if ($result) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $result seems to be never defined.
Loading history...
2107
            $num = $this->db->num_rows($result);
2108
            $i = 0;
2109
            while ($i < $num) { // For each notification couple defined (third party/actioncode)
2110
                $obj = $this->db->fetch_object($result);
2111
2112
                $sendto = $obj->firstname . " " . $obj->lastname . " <" . $obj->email . ">";
2113
                $actiondefid = $obj->adid;
2114
2115
                if (dol_strlen($sendto))
2116
                {
2117
                    include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
2118
                    $application = ($conf->global->MAIN_APPLICATION_TITLE ? $conf->global->MAIN_APPLICATION_TITLE : 'Dolibarr ERP/CRM');
2119
2120
                    $subject = '[' . $application . '] ' . $langs->transnoentitiesnoconv("DolibarrNotification");
2121
2122
                    $message = $langs->transnoentities("YouReceiveMailBecauseOfNotification", $application, $mysoc->name) . "\n";
2123
                    $message .= $langs->transnoentities("YouReceiveMailBecauseOfNotification2", $application, $mysoc->name) . "\n";
2124
                    $message .= "\n";
2125
                    $message .= $texte;
2126
                    // Add link
2127
                    $link = '';
2128
                    switch ($objet_type) {
2129
                        case 'ficheinter':
2130
                            $link = '/fichinter/card.php?id=' . $objet_id;
2131
                            break;
2132
                        case 'propal':
2133
                            $link = '/comm/propal.php?id=' . $objet_id;
2134
                            break;
2135
                        case 'facture':
2136
                            $link = '/compta/facture/card.php?facid=' . $objet_id;
2137
                            break;
2138
                        case 'order':
2139
                            $link = '/commande/card.php?facid=' . $objet_id;
2140
                            break;
2141
                        case 'order_supplier':
2142
                            $link = '/fourn/commande/card.php?facid=' . $objet_id;
2143
                            break;
2144
                    }
2145
                    // Define $urlwithroot
2146
                    $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim($dolibarr_main_url_root));
2147
                    $urlwithroot = $urlwithouturlroot . DOL_URL_ROOT; // This is to use external domain name found into config file
2148
                    //$urlwithroot=DOL_MAIN_URL_ROOT;                        // This is to use same domain name than current
2149
                    if ($link) {
2150
                        $message .= "\n" . $urlwithroot . $link;
2151
                    }
2152
2153
                    $filename = basename($file);
2154
2155
                    $mimefile = dol_mimetype($file);
2156
2157
                    $msgishtml = 0;
2158
2159
                    $replyto = $conf->notification->email_from;
2160
2161
                    $message = dol_nl2br($message);
2162
2163
                    if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) {
2164
                        $old_MAIN_MAIL_AUTOCOPY_TO = $conf->global->MAIN_MAIL_AUTOCOPY_TO;
2165
                        $conf->global->MAIN_MAIL_AUTOCOPY_TO = '';
2166
                    }
2167
                    $mailfile = new CMailFile(
2168
                        $subject,
2169
                        $sendto,
2170
                        $replyto,
2171
                        $message,
2172
                        array($file),
2173
                        array($mimefile),
2174
                        array($filename[count($filename) - 1]),
2175
                        '',
2176
                        '',
2177
                        0,
2178
                        $msgishtml
2179
                    );
2180
2181
                    if ($mailfile->sendfile()) {
2182
                        $now = dol_now();
2183
                        $sendto = htmlentities($sendto);
2184
2185
                        $sql = "INSERT INTO " . MAIN_DB_PREFIX . "notify (daten, fk_action, fk_contact, objet_type, objet_id, email)";
2186
                        $sql .= " VALUES ('" . $this->db->idate($now) . "', " . $actiondefid . ", " . $obj->cid . ", '" . $this->db->escape($objet_type) . "', " . $objet_id . ", '" . $this->db->escape($obj->email) . "')";
2187
                        dol_syslog("Notify::send sql=" . $sql);
2188
                        if (!$this->db->query($sql)) {
2189
                            dol_print_error($this->db);
2190
                        }
2191
                    } else {
2192
                        $this->error = $mailfile->error;
2193
                        //dol_syslog("Notify::send ".$this->error, LOG_ERR);
2194
                    }
2195
                    if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) {
2196
                        $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $old_MAIN_MAIL_AUTOCOPY_TO does not seem to be defined for all execution paths leading up to this point.
Loading history...
2197
                    }
2198
                }
2199
                $i++;
2200
            }
2201
            return $i;
2202
        } else {
2203
            $this->error = $this->db->error();
2204
            return -1;
2205
        }
2206
    }
2207
2208
    /**
2209
     *    Get array of all contacts for a ticket
2210
     *    Override method of file commonobject.class.php to add phone number
2211
     *
2212
     *    @param	int    	$statut 	Status of lines to get (-1=all)
2213
     *    @param	string 	$source 	Source of contact: external or thirdparty (llx_socpeople) or internal (llx_user)
2214
     *    @param	int    	$list   	0:Return array contains all properties, 1:Return array contains just id
2215
     *    @param    string  $code       Filter on this code of contact type ('SHIPPING', 'BILLING', ...)
2216
     *    @return 	array          		Array of contacts
2217
     */
2218
    public function listeContact($statut = -1, $source = 'external', $list = 0, $code = '')
2219
    {
2220
        global $langs;
2221
2222
        $tab = array();
2223
2224
        $sql = "SELECT ec.rowid, ec.statut  as statuslink, ec.fk_socpeople as id, ec.fk_c_type_contact"; // This field contains id of llx_socpeople or id of llx_user
2225
        if ($source == 'internal') {
2226
            $sql .= ", '-1' as socid, t.statut as statuscontact";
2227
        }
2228
2229
        if ($source == 'external' || $source == 'thirdparty') {
2230
            $sql .= ", t.fk_soc as socid, t.statut as statuscontact";
2231
        }
2232
2233
        $sql .= ", t.civility, t.lastname as lastname, t.firstname, t.email";
2234
        if ($source == 'internal') {
2235
            $sql .= ", t.office_phone as phone, t.user_mobile as phone_mobile";
2236
        }
2237
2238
        if ($source == 'external') {
2239
            $sql .= ", t.phone as phone, t.phone_mobile as phone_mobile, t.phone_perso as phone_perso";
2240
        }
2241
2242
        $sql .= ", tc.source, tc.element, tc.code, tc.libelle";
2243
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_type_contact tc";
2244
        $sql .= ", " . MAIN_DB_PREFIX . "element_contact ec";
2245
        if ($source == 'internal') {
2246
            $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "user t on ec.fk_socpeople = t.rowid";
2247
        }
2248
2249
        if ($source == 'external' || $source == 'thirdparty') {
2250
            $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "socpeople t on ec.fk_socpeople = t.rowid";
2251
        }
2252
2253
        $sql .= " WHERE ec.element_id =" . $this->id;
2254
        $sql .= " AND ec.fk_c_type_contact=tc.rowid";
2255
        $sql .= " AND tc.element='" . $this->db->escape($this->element) . "'";
2256
        if ($source == 'internal') {
2257
            $sql .= " AND tc.source = 'internal'";
2258
        }
2259
2260
        if ($source == 'external' || $source == 'thirdparty') {
2261
            $sql .= " AND tc.source = 'external'";
2262
        }
2263
2264
        $sql .= " AND tc.active=1";
2265
        if ($statut >= 0) {
2266
            $sql .= " AND ec.statut = '" . $statut . "'";
2267
        }
2268
2269
        $sql .= " ORDER BY t.lastname ASC";
2270
2271
        $resql = $this->db->query($sql);
2272
        if ($resql) {
2273
            $num = $this->db->num_rows($resql);
2274
            $i = 0;
2275
            while ($i < $num) {
2276
                $obj = $this->db->fetch_object($resql);
2277
2278
                if (!$list) {
2279
                    $transkey = "TypeContact_" . $obj->element . "_" . $obj->source . "_" . $obj->code;
2280
                    $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
2281
                    $tab[$i] = array(
2282
                            'source' => $obj->source,
2283
                            'socid' => $obj->socid,
2284
                            'id' => $obj->id,
2285
                            'nom' => $obj->lastname, // For backward compatibility
2286
                            'civility' => $obj->civility,
2287
                            'lastname' => $obj->lastname,
2288
                            'firstname' => $obj->firstname,
2289
                            'email' => $obj->email,
2290
                            'rowid' => $obj->rowid,
2291
                            'code' => $obj->code,
2292
                            'libelle' => $libelle_type,
2293
                            'status' => $obj->statuslink,
2294
                            'statuscontact'=>$obj->statuscontact,
2295
                            'fk_c_type_contact' => $obj->fk_c_type_contact,
2296
                            'phone' => $obj->phone,
2297
                            'phone_mobile' => $obj->phone_mobile);
2298
                } else {
2299
                    $tab[$i] = $obj->id;
2300
                }
2301
2302
                $i++;
2303
            }
2304
2305
            return $tab;
2306
        } else {
2307
            $this->error = $this->db->error();
2308
            dol_print_error($this->db);
2309
            return -1;
2310
        }
2311
    }
2312
2313
    /**
2314
     * Get a default reference.
2315
     *
2316
     * @param	Societe		$thirdparty		Thirdparty
2317
     * @return 	string   					Reference
2318
     */
2319
    public function getDefaultRef($thirdparty = '')
2320
    {
2321
        global $conf;
2322
2323
        $defaultref = '';
2324
        $modele = empty($conf->global->TICKET_ADDON) ? 'mod_ticket_simple' : $conf->global->TICKET_ADDON;
2325
2326
        // Search template files
2327
        $file = '';
2328
        $classname = '';
2329
        $filefound = 0;
2330
        $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2331
        foreach ($dirmodels as $reldir) {
2332
            $file = dol_buildpath($reldir . "core/modules/ticket/" . $modele . '.php', 0);
2333
            if (file_exists($file)) {
2334
                $filefound = 1;
2335
                $classname = $modele;
2336
                break;
2337
            }
2338
        }
2339
2340
        if ($filefound) {
2341
            $result = dol_include_once($reldir . "core/modules/ticket/" . $modele . '.php');
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $reldir seems to be defined by a foreach iteration on line 2331. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
2342
            $modTicket = new $classname;
2343
2344
            $defaultref = $modTicket->getNextValue($thirdparty, $this);
2345
        }
2346
2347
        if (is_numeric($defaultref) && $defaultref <= 0) {
2348
            $defaultref = '';
2349
        }
2350
2351
        return $defaultref;
2352
    }
2353
2354
2355
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2356
    /**
2357
     *  Return if at least one photo is available
2358
     *
2359
     *  @param      string      $sdir       Directory to scan
2360
     *  @return     boolean                 True if at least one photo is available, False if not
2361
     */
2362
    public function is_photo_available($sdir)
2363
    {
2364
        // phpcs:enable
2365
        include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
2366
2367
        global $conf;
2368
2369
        $dir = $sdir . '/';
2370
        $nbphoto = 0;
2371
2372
        $dir_osencoded = dol_osencode($dir);
2373
        if (file_exists($dir_osencoded)) {
2374
            $handle = opendir($dir_osencoded);
2375
            if (is_resource($handle)) {
2376
                while (($file = readdir($handle)) !== false) {
2377
                    if (!utf8_check($file)) {
2378
                        $file = utf8_encode($file);
2379
                    }
2380
                    // To be sure data is stored in UTF8 in memory
2381
                    if (dol_is_file($dir . $file)) {
2382
                        return true;
2383
                    }
2384
                }
2385
            }
2386
        }
2387
        return false;
2388
    }
2389
2390
2391
    /**
2392
     * Copy files into ticket directory
2393
     * Used for files linked into messages
2394
     *
2395
     * @return	void
2396
     */
2397
    public function copyFilesForTicket()
2398
    {
2399
        global $conf;
2400
2401
        // Create form object
2402
        include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php';
2403
        include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
2404
        include_once DOL_DOCUMENT_ROOT . '/core/lib/images.lib.php';
2405
2406
        $maxwidthsmall = 270;
2407
        $maxheightsmall = 150;
2408
        $maxwidthmini = 128;
2409
        $maxheightmini = 72;
2410
2411
        $formmail = new FormMail($this->db);
2412
2413
        $attachedfiles = $formmail->get_attached_files();
2414
2415
        $filepath = $attachedfiles['paths'];
2416
        $filename = $attachedfiles['names'];
2417
        $mimetype = $attachedfiles['mimes'];
2418
2419
        // Copy files into ticket directory
2420
        $destdir = $conf->ticket->dir_output . '/' . $this->ref;
2421
2422
        if (!dol_is_dir($destdir)) {
2423
            dol_mkdir($destdir);
2424
        }
2425
        foreach ($filename as $i => $val) {
2426
            $res = dol_move($filepath[$i], $destdir . '/' . $filename[$i]);
2427
            if (image_format_supported($destdir . '/' . $filename[$i]) == 1) {
2428
                // Create small thumbs for image (Ratio is near 16/9)
2429
                // Used on logon for example
2430
                $imgThumbSmall = vignette($destdir . '/' . $filename[$i], $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs");
2431
                // Create mini thumbs for image (Ratio is near 16/9)
2432
                // Used on menu or for setup page for example
2433
                $imgThumbMini = vignette($destdir . '/' . $filename[$i], $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs");
2434
            }
2435
            $formmail->remove_attached_files($i);
2436
        }
2437
    }
2438
2439
2440
    /**
2441
     * Add new message on a ticket (private area)
2442
     *
2443
     * @param   User    $user       User for action
2444
     * @param   string  $action     Action string
2445
     * @param   int     $private    1=Message is private. TODO Implement this. What does this means ?
2446
     * @return  int
2447
     */
2448
    public function newMessage($user, &$action, $private = 1)
2449
    {
2450
        global $mysoc, $conf, $langs;
2451
2452
        if (!class_exists('Contact')) {
2453
            include_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
2454
        }
2455
2456
        $contactstatic = new Contact($this->db);
2457
        $object = new Ticket($this->db);
2458
2459
        $error = 0;
2460
2461
        $ret = $object->fetch('', '', GETPOST('track_id', 'alpha'));
2462
2463
        $object->socid = $object->fk_soc;
2464
        $object->fetch_thirdparty();
2465
        if ($ret < 0) {
2466
            $error++;
2467
            array_push($this->errors, $langs->trans("ErrorTicketIsNotValid"));
2468
            $action = '';
2469
        }
2470
2471
        if (!GETPOST("message")) {
2472
            $error++;
2473
            array_push($this->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("message")));
2474
            $action = 'add_message';
2475
        }
2476
2477
        if (!$error) {
2478
            $object->subject = GETPOST('subject', 'alphanohtml');
2479
            $object->message = GETPOST("message", "none");
1 ignored issue
show
Documentation Bug introduced by
It seems like GETPOST('message', 'none') can also be of type string[]. However, the property $message is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
2480
            $object->private = GETPOST("private_message", "alpha");
2481
            $send_email = GETPOST('send_email', 'int');
2482
2483
            $id = $object->createTicketMessage($user);
2484
            if ($id <= 0) {
2485
                $error++;
2486
                $this->errors = $object->error;
0 ignored issues
show
Documentation Bug introduced by
It seems like $object->error of type string is incompatible with the declared type string[] of property $errors.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
2487
                $this->errors = $object->errors;
2488
                $action = 'add_message';
2489
            }
2490
2491
            if (!$error && $id > 0) {
2492
                setEventMessages($langs->trans('TicketMessageSuccessfullyAdded'), null, 'mesgs');
2493
2494
                /*
2495
                 * Send email to linked contacts
2496
                 */
2497
                if ($send_email > 0) {
2498
                    // Retrieve internal contact datas
2499
                    $internal_contacts = $object->getInfosTicketInternalContact();
2500
                    $sendto = array();
2501
                    if (is_array($internal_contacts) && count($internal_contacts) > 0) {
2502
                        // altairis: set default subject
2503
                        $label_title = empty($conf->global->MAIN_APPLICATION_TITLE) ? $mysoc->name : $conf->global->MAIN_APPLICATION_TITLE;
2504
                        $subject = GETPOST('subject') ? GETPOST('subject') : '[' . $label_title . '- ticket #' . $object->track_id . '] ' . $langs->trans('TicketNewMessage');
2505
2506
                        $message_intro = $langs->trans('TicketNotificationEmailBody', "#" . $object->id);
2507
                        $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : $conf->global->TICKET_MESSAGE_MAIL_SIGNATURE;
2508
2509
                        $message = $langs->trans('TicketMessageMailIntroText');
2510
                        $message .= "\n\n";
2511
                        $message .= GETPOST('message');
2512
2513
                        //  Coordonnées client
2514
                        $message .= "\n\n";
2515
                        $message .= "==============================================\n";
2516
                        $message .= !empty($object->thirdparty->name) ? $langs->trans('Thirdparty') . " : " . $object->thirdparty->name : '';
2517
                        $message .= !empty($object->thirdparty->town) ? "\n" . $langs->trans('Town') . " : " . $object->thirdparty->town : '';
2518
                        $message .= !empty($object->thirdparty->phone) ? "\n" . $langs->trans('Phone') . " : " . $object->thirdparty->phone : '';
2519
2520
                        // Build array to display recipient list
2521
                        foreach ($internal_contacts as $key => $info_sendto) {
2522
                            // altairis: avoid duplicate notifications
2523
                            if ($info_sendto['id'] == $user->id) {
2524
                                continue;
2525
                            }
2526
2527
                            if ($info_sendto['email'] != '') {
2528
                                if(!empty($info_sendto['email'])) $sendto[] = trim($info_sendto['firstname'] . " " . $info_sendto['lastname']) . " <" . $info_sendto['email'] . ">";
2529
2530
                                //Contact type
2531
                                $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1') . ' (' . strtolower($info_sendto['libelle']) . ')';
2532
                                $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient') . ' : ' . $recipient . "\n" : '');
2533
                            }
2534
                        }
2535
                        $message .= "\n";
2536
                        // URL ticket
2537
                        $url_internal_ticket = dol_buildpath('/ticket/card.php', 2) . '?track_id=' . $object->track_id;
2538
2539
                        // altairis: make html link on url
2540
                        $message .= "\n" . $langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal') . ' : ' . '<a href="' . $url_internal_ticket . '">' . $object->track_id . '</a>' . "\n";
2541
2542
                        // Add global email address recipient
2543
                        // altairis: use new TICKET_NOTIFICATION_EMAIL_TO configuration variable
2544
                        if ($conf->global->TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS && !in_array($conf->global->TICKET_NOTIFICATION_EMAIL_TO, $sendto)) {
2545
                            if(!empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO)) $sendto[] = $conf->global->TICKET_NOTIFICATION_EMAIL_TO;
2546
                        }
2547
2548
                        // altairis: dont try to send email if no recipient
2549
                        if (!empty($sendto)) {
2550
                            $this->sendTicketMessageByEmail($subject, $message, '', $sendto);
2551
                        }
2552
                    }
2553
2554
                    /*
2555
                     * Email for externals users if not private
2556
                     */
2557
                    if (empty($object->private)) {
2558
                        // Retrieve email of all contacts (external)
2559
                        $external_contacts = $object->getInfosTicketExternalContact();
2560
2561
                        // If no contact, get email from thirdparty
2562
                        if (is_array($external_contacts) && count($external_contacts) === 0) {
2563
                            if (!empty($object->fk_soc)) {
2564
                                $object->fetch_thirdparty($object->fk_soc);
2565
                                $array_company = array(array('firstname' => '', 'lastname' => $object->thirdparty->name, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));
2566
                                $external_contacts = array_merge($external_contacts, $array_company);
2567
                            } elseif (empty($object->fk_soc) && !empty($object->origin_email)) {
2568
                                $array_external = array(array('firstname' => '', 'lastname' => $object->origin_email, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));
2569
                                $external_contacts = array_merge($external_contacts, $array_external);
2570
                            }
2571
                        }
2572
2573
                        $sendto = array();
2574
                        if (is_array($external_contacts) && count($external_contacts) > 0) {
2575
                            // altairis: get default subject for email to external contacts
2576
                            $label_title = empty($conf->global->MAIN_APPLICATION_TITLE) ? $mysoc->name : $conf->global->MAIN_APPLICATION_TITLE;
2577
                            $subject = GETPOST('subject') ? GETPOST('subject') : '[' . $label_title . '- ticket #' . $object->track_id . '] ' . $langs->trans('TicketNewMessage');
2578
2579
                            $message_intro = GETPOST('mail_intro') ? GETPOST('mail_intro') : $conf->global->TICKET_MESSAGE_MAIL_INTRO;
2580
                            $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : $conf->global->TICKET_MESSAGE_MAIL_SIGNATURE;
2581
2582
                            // We put intro after
2583
                            $message = GETPOST('message');
2584
                            $message .= "\n\n";
2585
2586
                            foreach ($external_contacts as $key => $info_sendto) {
2587
                                // altairis: avoid duplicate emails to external contacts
2588
                                if ($info_sendto['id'] == $user->contactid) {
2589
                                    continue;
2590
                                }
2591
2592
                                if ($info_sendto['email'] != '' && $info_sendto['email'] != $object->origin_email) {
2593
                                    if(!empty($info_sendto['email'])) $sendto[] = trim($info_sendto['firstname'] . " " . $info_sendto['lastname']) . " <" . $info_sendto['email'] . ">";
2594
2595
                                    $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1') . ' (' . strtolower($info_sendto['libelle']) . ')';
2596
                                    $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient') . ' : ' . $recipient . "\n" : '');
2597
                                }
2598
                            }
2599
2600
                            // If public interface is not enable, use link to internal page into mail
2601
                            $url_public_ticket = (!empty($conf->global->TICKET_ENABLE_PUBLIC_INTERFACE) ?
2602
                                (!empty($conf->global->TICKET_URL_PUBLIC_INTERFACE) ?
2603
                                    $conf->global->TICKET_URL_PUBLIC_INTERFACE . '/view.php' :
2604
                                    dol_buildpath('/public/ticket/view.php', 2)
2605
                                    ) :
2606
                                dol_buildpath('/ticket/card.php', 2)
2607
                                ) . '?track_id=' . $object->track_id;
2608
                                $message .= "\n" . $langs->trans('TicketNewEmailBodyInfosTrackUrlCustomer') . ' : ' . '<a href="' . $url_public_ticket . '">' . $object->track_id . '</a>' . "\n";
2609
2610
                                // Build final message
2611
                                $message = $message_intro . $message;
1 ignored issue
show
Bug introduced by
Are you sure $message_intro of type mixed|string|string[] can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2611
                                $message = /** @scrutinizer ignore-type */ $message_intro . $message;
Loading history...
2612
2613
                                // Add signature
2614
                                $message .= '<br>' . $message_signature;
1 ignored issue
show
Bug introduced by
Are you sure $message_signature of type mixed|string|string[] can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2614
                                $message .= '<br>' . /** @scrutinizer ignore-type */ $message_signature;
Loading history...
2615
2616
                                if (!empty($object->origin_email)) {
2617
                                    $sendto[] = $object->origin_email;
2618
                                }
2619
2620
                                if ($object->fk_soc > 0 && ! in_array($object->origin_email, $sendto)) {
2621
                                    $object->socid = $object->fk_soc;
2622
                                    $object->fetch_thirdparty();
2623
                                    if(!empty($object->thirdparty->email)) $sendto[] = $object->thirdparty->email;
2624
                                }
2625
2626
                                // altairis: Add global email address reciepient
2627
                                if ($conf->global->TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS && !in_array($conf->global->TICKET_NOTIFICATION_EMAIL_TO, $sendto)) {
2628
                                    if(!empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO)) $sendto[] = $conf->global->TICKET_NOTIFICATION_EMAIL_TO;
2629
                                }
2630
2631
                                // altairis: dont try to send email when no recipient
2632
                                if (!empty($sendto)) {
2633
                                    $this->sendTicketMessageByEmail($subject, $message, '', $sendto);
2634
                                }
2635
                        }
2636
                    }
2637
                }
2638
2639
                $object->copyFilesForTicket();
2640
2641
                // Set status to "answered" if not set yet, only for internal users
2642
                if ($object->fk_statut < 3 && !$user->societe_id) {
1 ignored issue
show
Deprecated Code introduced by
The property User::$societe_id has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

2642
                if ($object->fk_statut < 3 && !/** @scrutinizer ignore-deprecated */ $user->societe_id) {
Loading history...
2643
                    $object->setStatut(3);
2644
                }
2645
2646
                return 1;
2647
            } else {
2648
                setEventMessages($object->error, $object->errors, 'errors');
2649
                return -1;
2650
            }
2651
        } else {
2652
            setEventMessages($this->error, $this->errors, 'errors');
2653
            return -1;
2654
        }
2655
    }
2656
2657
2658
    /**
2659
     * Send ticket by email to linked contacts
2660
     *
2661
     * @param string $subject          Email subject
2662
     * @param string $message          Email message
2663
     * @param int    $send_internal_cc Receive a copy on internal email ($conf->global->TICKET_NOTIFICATION_EMAIL_FROM)
2664
     * @param array  $array_receiver   Array of receiver. exemple array('name' => 'John Doe', 'email' => '[email protected]', etc...)
2665
     * @return void
2666
     */
2667
    public function sendTicketMessageByEmail($subject, $message, $send_internal_cc = 0, $array_receiver = array())
2668
    {
2669
        global $conf, $langs;
2670
2671
        if ($conf->global->TICKET_DISABLE_ALL_MAILS) {
2672
            dol_syslog(get_class($this) . '::sendTicketMessageByEmail: Emails are disable into ticket setup by option TICKETSUP_DISABLE_ALL_MAILS', LOG_WARNING);
2673
            return '';
2674
        }
2675
2676
        $langs->load("mails");
2677
2678
        include_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
2679
        //$contactstatic = new Contact($this->db);
2680
2681
        // If no receiver defined, load all ticket linked contacts
2682
        if (!is_array($array_receiver) || !count($array_receiver) > 0) {
1 ignored issue
show
introduced by
The condition is_array($array_receiver) is always true.
Loading history...
2683
            $array_receiver = $this->getInfosTicketInternalContact();
2684
            $array_receiver = array_merge($array_receiver, $this->getInfosTicketExternalContact());
2685
        }
2686
2687
        if ($send_internal_cc) {
2688
            $sendtocc = $conf->global->TICKET_NOTIFICATION_EMAIL_FROM;
2689
        }
2690
2691
        $from = $conf->global->TICKET_NOTIFICATION_EMAIL_FROM;
2692
        if (is_array($array_receiver) && count($array_receiver) > 0) {
2693
            foreach ($array_receiver as $key => $receiver) {
2694
                // Create form object
2695
                include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php';
2696
                $formmail = new FormMail($this->db);
2697
2698
                $attachedfiles = $formmail->get_attached_files();
2699
                $filepath = $attachedfiles['paths'];
2700
                $filename = $attachedfiles['names'];
2701
                $mimetype = $attachedfiles['mimes'];
2702
2703
                $message_to_send = dol_nl2br($message);
2704
2705
                // Envoi du mail
2706
                if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) {
2707
                    $old_MAIN_MAIL_AUTOCOPY_TO = $conf->global->MAIN_MAIL_AUTOCOPY_TO;
2708
                    $conf->global->MAIN_MAIL_AUTOCOPY_TO = '';
2709
                }
2710
                include_once DOL_DOCUMENT_ROOT . '/core/class/CMailFile.class.php';
2711
                $mailfile = new CMailFile($subject, $receiver, $from, $message_to_send, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, -1);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $deliveryreceipt seems to be never defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable $sendtocc does not seem to be defined for all execution paths leading up to this point.
Loading history...
2712
                if ($mailfile->error) {
2713
                    setEventMessages($mailfile->error, null, 'errors');
2714
                } else {
2715
                    $result = $mailfile->sendfile();
2716
                    if ($result) {
2717
                        setEventMessages($langs->trans('MailSuccessfulySent', $mailfile->getValidAddress($from, 2), $mailfile->getValidAddress($receiver, 2)), null, 'mesgs');
2718
                    } else {
2719
                        $langs->load("other");
2720
                        if ($mailfile->error) {
2721
                            setEventMessages($langs->trans('ErrorFailedToSendMail', $from, $receiver), null, 'errors');
2722
                            dol_syslog($langs->trans('ErrorFailedToSendMail', $from, $receiver) . ' : ' . $mailfile->error);
2723
                        } else {
2724
                            setEventMessages('No mail sent. Feature is disabled by option MAIN_DISABLE_ALL_MAILS', null, 'errors');
2725
                        }
2726
                    }
2727
                }
2728
                if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) {
2729
                    $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $old_MAIN_MAIL_AUTOCOPY_TO does not seem to be defined for all execution paths leading up to this point.
Loading history...
2730
                }
2731
            }
2732
        } else {
2733
            $langs->load("other");
2734
            setEventMessages($langs->trans('ErrorMailRecipientIsEmptyForSendTicketMessage'), null, 'warnings');
2735
        }
2736
    }
2737
}
2738
2739
2740
/**
2741
 * Ticket line Class
2742
 */
2743
class TicketsLine
2744
{
2745
    /**
2746
     * @var int ID
2747
     */
2748
    public $id;
2749
2750
    /**
2751
     * @var string  $ref    Ticket reference
2752
     */
2753
    public $ref;
2754
2755
    /**
2756
     * Hash to identify ticket
2757
     */
2758
    public $track_id;
2759
2760
    /**
2761
     * @var int Thirdparty ID
2762
     */
2763
    public $fk_soc;
2764
2765
    /**
2766
     * Project ID
2767
     */
2768
    public $fk_project;
2769
2770
    /**
2771
     * Person email who have create ticket
2772
     */
2773
    public $origin_email;
2774
2775
    /**
2776
     * User id who have create ticket
2777
     */
2778
    public $fk_user_create;
2779
2780
    /**
2781
     * User id who have ticket assigned
2782
     */
2783
    public $fk_user_assign;
2784
2785
    /**
2786
     * Ticket subject
2787
     */
2788
    public $subject;
2789
2790
    /**
2791
     * Ticket message
2792
     */
2793
    public $message;
2794
2795
    /**
2796
      * Ticket statut
2797
     */
2798
    public $fk_statut;
2799
2800
    /**
2801
     * State resolution
2802
     */
2803
    public $resolution;
2804
2805
    /**
2806
     * Progress in percent
2807
     */
2808
    public $progress;
2809
2810
    /**
2811
     * Duration for ticket
2812
     */
2813
    public $timing;
2814
2815
    /**
2816
     * Type code
2817
     */
2818
    public $type_code;
2819
2820
    /**
2821
     * Category code
2822
     */
2823
    public $category_code;
2824
2825
    /**
2826
     * Severity code
2827
     */
2828
    public $severity_code;
2829
2830
    /**
2831
     * Type label
2832
     */
2833
    public $type_label;
2834
2835
    /**
2836
     * Category label
2837
     */
2838
    public $category_label;
2839
2840
    /**
2841
     * Severity label
2842
     */
2843
    public $severity_label;
2844
2845
    /**
2846
      * Creation date
2847
     */
2848
    public $datec = '';
2849
2850
    /**
2851
      * Read date
2852
     */
2853
    public $date_read = '';
2854
2855
    /**
2856
      * Close ticket date
2857
     */
2858
    public $date_close = '';
2859
}
2860