commonitilobject.class.php 285 KB
Newer Older
moyooo's avatar
moyooo committed
1
<?php
2
3
4
/**
 * ---------------------------------------------------------------------
 * GLPI - Gestionnaire Libre de Parc Informatique
5
 * Copyright (C) 2015-2021 Teclib' and contributors.
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 *
 * http://glpi-project.org
 *
 * based on GLPI - Gestionnaire Libre de Parc Informatique
 * Copyright (C) 2003-2014 by the INDEPNET Development Team.
 *
 * ---------------------------------------------------------------------
 *
 * LICENSE
 *
 * This file is part of GLPI.
 *
 * GLPI is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * GLPI is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with GLPI. If not, see <http://www.gnu.org/licenses/>.
 * ---------------------------------------------------------------------
moyooo's avatar
moyooo committed
31
32
33
 */

if (!defined('GLPI_ROOT')) {
Stefan Weil's avatar
Stefan Weil committed
34
   die("Sorry. You can't access this file directly");
moyooo's avatar
moyooo committed
35
36
}

yllen's avatar
yllen committed
37
38
39
/**
 * CommonITILObject Class
**/
moyooo's avatar
moyooo committed
40
41
42
abstract class CommonITILObject extends CommonDBTM {

   /// Users by type
43
   protected $users       = [];
yllen's avatar
yllen committed
44
   public $userlinkclass  = '';
moyooo's avatar
moyooo committed
45
   /// Groups by type
46
   protected $groups      = [];
moyooo's avatar
moyooo committed
47
48
   public $grouplinkclass = '';

49
   /// Suppliers by type
50
   protected $suppliers      = [];
51
52
   public $supplierlinkclass = '';

moyooo's avatar
moyooo committed
53
54
55
   /// Use user entity to select entity of the object
   protected $userentity_oncreate = false;

moyooo's avatar
moyooo committed
56
57

   /// From CommonDBTM
Johan Cwiklinski's avatar
Johan Cwiklinski committed
58
   public $notificationqueueonaction = true;
moyooo's avatar
moyooo committed
59

yllen's avatar
yllen committed
60
61
62
63
   const MATRIX_FIELD         = '';
   const URGENCY_MASK_FIELD   = '';
   const IMPACT_MASK_FIELD    = '';
   const STATUS_MATRIX_FIELD  = '';
moyooo's avatar
moyooo committed
64

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

   // STATUS
   const INCOMING      = 1; // new
   const ASSIGNED      = 2; // assign
   const PLANNED       = 3; // plan
   const WAITING       = 4; // waiting
   const SOLVED        = 5; // solved
   const CLOSED        = 6; // closed
   const ACCEPTED      = 7; // accepted
   const OBSERVED      = 8; // observe
   const EVALUATION    = 9; // evaluation
   const APPROVAL      = 10; // approbation
   const TEST          = 11; // test
   const QUALIFICATION = 12; // qualification

80
   const NO_TIMELINE       = -1;
81
82
83
84
85
86
87
   const TIMELINE_NOTSET   = 0;
   const TIMELINE_LEFT     = 1;
   const TIMELINE_MIDLEFT  = 2;
   const TIMELINE_MIDRIGHT = 3;
   const TIMELINE_RIGHT    = 4;


yllen's avatar
yllen committed
88

yllen's avatar
CS    
yllen committed
89
   function post_getFromDB() {
90
91
92
      $this->loadActors();
   }

yllen's avatar
CS    
yllen committed
93
94

   /**
Anael Mobilia's avatar
Anael Mobilia committed
95
    * @since 0.84
yllen's avatar
CS    
yllen committed
96
   **/
97
   function loadActors() {
yllen's avatar
CS    
yllen committed
98

moyooo's avatar
moyooo committed
99
      if (!empty($this->grouplinkclass)) {
yllen's avatar
CS    
yllen committed
100
         $class        = new $this->grouplinkclass();
moyooo's avatar
moyooo committed
101
102
         $this->groups = $class->getActors($this->fields['id']);
      }
yllen's avatar
yllen committed
103

moyooo's avatar
moyooo committed
104
      if (!empty($this->userlinkclass)) {
yllen's avatar
CS    
yllen committed
105
         $class        = new $this->userlinkclass();
moyooo's avatar
moyooo committed
106
107
         $this->users  = $class->getActors($this->fields['id']);
      }
108
109

      if (!empty($this->supplierlinkclass)) {
yllen's avatar
CS    
yllen committed
110
         $class            = new $this->supplierlinkclass();
111
112
         $this->suppliers  = $class->getActors($this->fields['id']);
      }
moyooo's avatar
moyooo committed
113
114
   }

yllen's avatar
CS    
yllen committed
115

moyooo's avatar
moyooo committed
116
117
118
   /**
    * Retrieve an item from the database with datas associated (hardwares)
    *
Cédric Anne's avatar
Cédric Anne committed
119
120
    * @param integer $ID          ID of the item to get
    * @param boolean $purecontent true : nothing change / false : convert to HTML display
moyooo's avatar
moyooo committed
121
    *
Cédric Anne's avatar
Cédric Anne committed
122
    * @return boolean true if succeed else false
moyooo's avatar
moyooo committed
123
   **/
yllen's avatar
CS    
yllen committed
124
   function getFromDBwithData($ID, $purecontent) {
moyooo's avatar
moyooo committed
125
126
127

      if ($this->getFromDB($ID)) {
         if (!$purecontent) {
yllen's avatar
yllen committed
128
129
            $this->fields["content"] = nl2br(preg_replace("/\r\n\r\n/", "\r\n",
                                             $this->fields["content"]));
moyooo's avatar
moyooo committed
130
131
132
133
134
135
136
137
         }
         $this->getAdditionalDatas();
         return true;
      }
      return false;
   }


yllen's avatar
yllen committed
138
   function getAdditionalDatas() {
moyooo's avatar
moyooo committed
139
140
   }

yllen's avatar
yllen committed
141

142
143
144
145
146
   /**
    * Can manage actors
    *
    * @return boolean
    */
yllen's avatar
yllen committed
147
   function canAdminActors() {
148
149
150
151
      if (isset($this->fields['is_deleted']) && $this->fields['is_deleted'] == 1) {
         return false;
      }
      return Session::haveRight(static::$rightname, UPDATE);
moyooo's avatar
moyooo committed
152
153
   }

yllen's avatar
yllen committed
154

155
156
157
158
159
   /**
    * Can assign object
    *
    * @return boolean
    */
yllen's avatar
yllen committed
160
   function canAssign() {
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
      if (isset($this->fields['is_deleted']) && ($this->fields['is_deleted'] == 1)
          || isset($this->fields['status']) && in_array($this->fields['status'], $this->getClosedStatusArray())
      ) {
         return false;
      }
      return Session::haveRight(static::$rightname, UPDATE);
   }


   /**
    * Can be assigned to me
    *
    * @return boolean
    */
   function canAssignToMe() {
      if (isset($this->fields['is_deleted']) && $this->fields['is_deleted'] == 1
         || isset($this->fields['status']) && in_array($this->fields['status'], $this->getClosedStatusArray())
      ) {
         return false;
      }
      return Session::haveRight(static::$rightname, UPDATE);
moyooo's avatar
moyooo committed
182
183
   }

Cédric Anne's avatar
Cédric Anne committed
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199

   /**
    * Is the current user have right to approve solution of the current ITIL object.
    *
    * @since 9.4.0
    *
    * @return boolean
    */
   function canApprove() {

      return (($this->fields["users_id_recipient"] === Session::getLoginUserID())
              || $this->isUser(CommonITILActor::REQUESTER, Session::getLoginUserID())
              || (isset($_SESSION["glpigroups"])
                  && $this->haveAGroup(CommonITILActor::REQUESTER, $_SESSION["glpigroups"])));
   }

cconard96's avatar
cconard96 committed
200
201
202
   /**
    * Is the current user have right to add followups to the current ITIL Object ?
    *
Cédric Anne's avatar
Cédric Anne committed
203
204
    * @since 9.4.0
    *
cconard96's avatar
cconard96 committed
205
    * @return boolean
Cédric Anne's avatar
Cédric Anne committed
206
    */
cconard96's avatar
cconard96 committed
207
   function canAddFollowups() {
208
209
210
      return ((Session::haveRight("followup", ITILFollowup::ADDMYTICKET)
               && ($this->isUser(CommonITILActor::REQUESTER, Session::getLoginUserID())
                   || (isset($this->fields["users_id_recipient"])
Cédric Anne's avatar
Cédric Anne committed
211
                        && ($this->fields["users_id_recipient"] == Session::getLoginUserID()))))
212
213
214
215
216
217
218
              || Session::haveRight('followup', ITILFollowup::ADDALLTICKET)
              || (Session::haveRight('followup', ITILFollowup::ADDGROUPTICKET)
                  && isset($_SESSION["glpigroups"])
                  && $this->haveAGroup(CommonITILActor::REQUESTER, $_SESSION['glpigroups']))
              || $this->isUser(CommonITILActor::ASSIGN, Session::getLoginUserID())
              || (isset($_SESSION["glpigroups"])
                  && $this->haveAGroup(CommonITILActor::ASSIGN, $_SESSION['glpigroups'])));
cconard96's avatar
cconard96 committed
219
220
   }

yllen's avatar
yllen committed
221

222
223
224
225
226
227
228
229
230
231
232
   /**
    * Does current user have right to solve the current item?
    *
    * @return boolean
   **/
   function canSolve() {

      return ((Session::haveRight(static::$rightname, UPDATE)
               || $this->isUser(CommonITILActor::ASSIGN, Session::getLoginUserID())
               || (isset($_SESSION["glpigroups"])
                   && $this->haveAGroup(CommonITILActor::ASSIGN, $_SESSION["glpigroups"])))
233
              && static::isAllowedStatus($this->fields['status'], self::SOLVED)
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
              // No edition on closed status
              && !in_array($this->fields['status'], $this->getClosedStatusArray()));
   }

   /**
    * Does current user have right to solve the current item; if it was not closed?
    *
    * @return boolean
   **/
   function maySolve() {

      return ((Session::haveRight(static::$rightname, UPDATE)
               || $this->isUser(CommonITILActor::ASSIGN, Session::getLoginUserID())
               || (isset($_SESSION["glpigroups"])
                   && $this->haveAGroup(CommonITILActor::ASSIGN, $_SESSION["glpigroups"])))
249
              && static::isAllowedStatus($this->fields['status'], self::SOLVED));
250
251
252
   }


cconard96's avatar
cconard96 committed
253
254
255
   /**
    * Get the ITIL object closed, solved or waiting status list
    *
Cédric Anne's avatar
Cédric Anne committed
256
    * @since 9.4.0
cconard96's avatar
cconard96 committed
257
    *
Johan Cwiklinski's avatar
Johan Cwiklinski committed
258
    * @return array
Cédric Anne's avatar
Cédric Anne committed
259
    */
cconard96's avatar
cconard96 committed
260
261
262
263
   static function getReopenableStatusArray() {
      return [self::CLOSED, self::SOLVED, self::WAITING];
   }

264

moyooo's avatar
moyooo committed
265
   /**
moyooo's avatar
moyooo committed
266
    * Is a user linked to the object ?
moyooo's avatar
moyooo committed
267
    *
Cédric Anne's avatar
Cédric Anne committed
268
269
    * @param integer $type     type to search (see constants)
    * @param integer $users_id user ID
moyooo's avatar
moyooo committed
270
271
272
273
274
    *
    * @return boolean
   **/
   function isUser($type, $users_id) {

remi's avatar
remi committed
275
276
277
278
279
280
      if (isset($this->users[$type])) {
         foreach ($this->users[$type] as $data) {
            if ($data['users_id'] == $users_id) {
               return true;
            }
         }
moyooo's avatar
moyooo committed
281
282
283
284
285
286
      }

      return false;
   }


287
288
289
   /**
    * Is a group linked to the object ?
    *
Cédric Anne's avatar
Cédric Anne committed
290
291
    * @param integer $type      type to search (see constants)
    * @param integer $groups_id group ID
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
    *
    * @return boolean
   **/
   function isGroup($type, $groups_id) {

      if (isset($this->groups[$type])) {
         foreach ($this->groups[$type] as $data) {
            if ($data['groups_id'] == $groups_id) {
               return true;
            }
         }
      }
      return false;
   }


   /**
    * Is a supplier linked to the object ?
    *
Anael Mobilia's avatar
Anael Mobilia committed
311
    * @since 0.84
yllen's avatar
CS    
yllen committed
312
    *
Cédric Anne's avatar
Cédric Anne committed
313
314
    * @param integer $type         type to search (see constants)
    * @param integer $suppliers_id supplier ID
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
    *
    * @return boolean
   **/
   function isSupplier($type, $suppliers_id) {

      if (isset($this->suppliers[$type])) {
         foreach ($this->suppliers[$type] as $data) {
            if ($data['suppliers_id'] == $suppliers_id) {
               return true;
            }
         }
      }
      return false;
   }

yllen's avatar
CS    
yllen committed
330

moyooo's avatar
moyooo committed
331
   /**
moyooo's avatar
moyooo committed
332
    * get users linked to a object
moyooo's avatar
moyooo committed
333
    *
Cédric Anne's avatar
Cédric Anne committed
334
    * @param integer $type type to search (see constants)
moyooo's avatar
moyooo committed
335
336
337
338
339
340
341
342
343
    *
    * @return array
   **/
   function getUsers($type) {

      if (isset($this->users[$type])) {
         return $this->users[$type];
      }

344
      return [];
moyooo's avatar
moyooo committed
345
346
347
348
   }


   /**
moyooo's avatar
moyooo committed
349
    * get groups linked to a object
moyooo's avatar
moyooo committed
350
    *
Cédric Anne's avatar
Cédric Anne committed
351
    * @param integer $type type to search (see constants)
moyooo's avatar
moyooo committed
352
353
354
355
356
357
358
359
360
    *
    * @return array
   **/
   function getGroups($type) {

      if (isset($this->groups[$type])) {
         return $this->groups[$type];
      }

361
      return [];
moyooo's avatar
moyooo committed
362
363
   }

yllen's avatar
CS    
yllen committed
364

moyooo's avatar
moyooo committed
365
   /**
yllen's avatar
CS    
yllen committed
366
367
    * get users linked to an object including groups ones
    *
Anael Mobilia's avatar
Anael Mobilia committed
368
    * @since 0.85
moyooo's avatar
moyooo committed
369
    *
Cédric Anne's avatar
Cédric Anne committed
370
    * @param integer $type type to search (see constants)
moyooo's avatar
moyooo committed
371
372
    *
    * @return array
yllen's avatar
CS    
yllen committed
373
   **/
moyooo's avatar
moyooo committed
374
   function getAllUsers ($type) {
yllen's avatar
CS    
yllen committed
375

376
      $users = [];
moyooo's avatar
moyooo committed
377
378
379
380
381
382
383
384
385
386
387
388
389
      foreach ($this->getUsers($type) as $link) {
         $users[$link['users_id']] = $link['users_id'];
      }

      foreach ($this->getGroups($type) as $link) {
         $gusers = Group_User::getGroupUsers($link['groups_id']);
         foreach ($gusers as $user) {
            $users[$user['id']] = $user['id'];
         }
      }

      return $users;
   }
yllen's avatar
yllen committed
390

yllen's avatar
CS    
yllen committed
391

392
393
394
   /**
    * get suppliers linked to a object
    *
Anael Mobilia's avatar
Anael Mobilia committed
395
    * @since 0.84
yllen's avatar
CS    
yllen committed
396
    *
Cédric Anne's avatar
Cédric Anne committed
397
    * @param integer $type type to search (see constants)
398
399
400
401
402
403
404
405
    *
    * @return array
   **/
   function getSuppliers($type) {

      if (isset($this->suppliers[$type])) {
         return $this->suppliers[$type];
      }
moyooo's avatar
moyooo committed
406

407
      return [];
408
   }
yllen's avatar
CS    
yllen committed
409

yllen's avatar
yllen committed
410

moyooo's avatar
moyooo committed
411
   /**
moyooo's avatar
moyooo committed
412
    * count users linked to object by type or global
moyooo's avatar
moyooo committed
413
    *
Cédric Anne's avatar
Cédric Anne committed
414
    * @param integer $type type to search (see constants) / 0 for all (default 0)
moyooo's avatar
moyooo committed
415
    *
yllen's avatar
yllen committed
416
    * @return integer
moyooo's avatar
moyooo committed
417
   **/
418
   function countUsers($type = 0) {
moyooo's avatar
moyooo committed
419

yllen's avatar
gettext    
yllen committed
420
      if ($type > 0) {
moyooo's avatar
moyooo committed
421
         if (isset($this->users[$type])) {
yllen's avatar
yllen committed
422
            return count($this->users[$type]);
moyooo's avatar
moyooo committed
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
         }

      } else {
         if (count($this->users)) {
            $count = 0;
            foreach ($this->users as $u) {
               $count += count($u);
            }
            return $count;
         }
      }
      return 0;
   }


   /**
moyooo's avatar
moyooo committed
439
    * count groups linked to object by type or global
moyooo's avatar
moyooo committed
440
    *
Cédric Anne's avatar
Cédric Anne committed
441
    * @param integer $type type to search (see constants) / 0 for all (default 0)
moyooo's avatar
moyooo committed
442
    *
yllen's avatar
yllen committed
443
    * @return integer
moyooo's avatar
moyooo committed
444
   **/
445
   function countGroups($type = 0) {
moyooo's avatar
moyooo committed
446

yllen's avatar
gettext    
yllen committed
447
      if ($type > 0) {
moyooo's avatar
moyooo committed
448
         if (isset($this->groups[$type])) {
yllen's avatar
yllen committed
449
            return count($this->groups[$type]);
moyooo's avatar
moyooo committed
450
451
452
453
454
455
456
457
458
459
460
461
462
463
         }

      } else {
         if (count($this->groups)) {
            $count = 0;
            foreach ($this->groups as $u) {
               $count += count($u);
            }
            return $count;
         }
      }
      return 0;
   }

yllen's avatar
yllen committed
464

moyooo's avatar
moyooo committed
465
   /**
466
    * count suppliers linked to object by type or global
moyooo's avatar
moyooo committed
467
    *
Anael Mobilia's avatar
Anael Mobilia committed
468
    * @since 0.84
yllen's avatar
CS    
yllen committed
469
    *
Cédric Anne's avatar
Cédric Anne committed
470
    * @param integer $type type to search (see constants) / 0 for all (default 0)
moyooo's avatar
moyooo committed
471
    *
472
    * @return integer
moyooo's avatar
moyooo committed
473
   **/
474
   function countSuppliers($type = 0) {
moyooo's avatar
moyooo committed
475

476
477
478
479
480
481
482
483
484
485
      if ($type > 0) {
         if (isset($this->suppliers[$type])) {
            return count($this->suppliers[$type]);
         }

      } else {
         if (count($this->suppliers)) {
            $count = 0;
            foreach ($this->suppliers as $u) {
               $count += count($u);
remi's avatar
remi committed
486
            }
487
            return $count;
remi's avatar
remi committed
488
         }
moyooo's avatar
moyooo committed
489
      }
490
      return 0;
moyooo's avatar
moyooo committed
491
492
493
494
   }


   /**
moyooo's avatar
moyooo committed
495
    * Is one of groups linked to the object ?
moyooo's avatar
moyooo committed
496
    *
Cédric Anne's avatar
Cédric Anne committed
497
498
    * @param integer $type   type to search (see constants)
    * @param array   $groups groups IDs
moyooo's avatar
moyooo committed
499
500
501
    *
    * @return boolean
   **/
yllen's avatar
yllen committed
502
   function haveAGroup($type, array $groups) {
moyooo's avatar
moyooo committed
503

yllen's avatar
gettext    
yllen committed
504
505
506
      if (is_array($groups) && count($groups)
          && isset($this->groups[$type])) {

moyooo's avatar
moyooo committed
507
         foreach ($groups as $groups_id) {
remi's avatar
remi committed
508
            foreach ($this->groups[$type] as $data) {
yllen's avatar
gettext    
yllen committed
509
               if ($data['groups_id'] == $groups_id) {
remi's avatar
remi committed
510
511
                  return true;
               }
moyooo's avatar
moyooo committed
512
513
514
515
516
517
            }
         }
      }
      return false;
   }

yllen's avatar
yllen committed
518

moyooo's avatar
moyooo committed
519
520
521
   /**
    * Get Default actor when creating the object
    *
Cédric Anne's avatar
Cédric Anne committed
522
    * @param integer $type type to search (see constants)
moyooo's avatar
moyooo committed
523
524
525
526
    *
    * @return boolean
   **/
   function getDefaultActor($type) {
yllen's avatar
yllen committed
527

moyooo's avatar
moyooo committed
528
      /// TODO own_ticket -> own_itilobject
529
      if ($type == CommonITILActor::ASSIGN) {
moyooo's avatar
moyooo committed
530
         if (Session::haveRight("ticket", Ticket::OWN)) {
yllen's avatar
yllen committed
531
            return Session::getLoginUserID();
moyooo's avatar
moyooo committed
532
533
534
535
536
         }
      }
      return 0;
   }

yllen's avatar
CS    
yllen committed
537

moyooo's avatar
moyooo committed
538
539
540
   /**
    * Get Default actor when creating the object
    *
Cédric Anne's avatar
Cédric Anne committed
541
    * @param integer $type type to search (see constants)
moyooo's avatar
moyooo committed
542
543
544
545
    *
    * @return boolean
   **/
   function getDefaultActorRightSearch($type) {
yllen's avatar
yllen committed
546

547
      if ($type == CommonITILActor::ASSIGN) {
moyooo's avatar
moyooo committed
548
549
550
551
         return "own_ticket";
      }
      return "all";
   }
moyooo's avatar
moyooo committed
552

yllen's avatar
CS    
yllen committed
553

554
   /**
555
    * Count active ITIL Objects
556
    *
557
    * @since 9.3.1
yllen's avatar
yllen committed
558
    *
559
560
561
    * @param CommonITILActor $linkclass Link class instance
    * @param integer         $id        Item ID
    * @param integer         $role      ITIL role
562
    *
563
    * @return integer
564
   **/
565
   private function countActiveObjectsFor(CommonITILActor $linkclass, $id, $role) {
yllen's avatar
CS    
yllen committed
566

567
568
569
      $itemtable = $this->getTable();
      $itemfk    = $this->getForeignKeyField();
      $linktable = $linkclass->getTable();
570
      $field     = $linkclass::$items_id_2;
571

572
573
      return countElementsInTable(
         [$itemtable, $linktable], [
574
575
576
            "$linktable.$itemfk"    => new \QueryExpression(DBmysql::quoteName("$itemtable.id")),
            "$linktable.$field"     => $id,
            "$linktable.type"       => $role,
577
578
579
580
581
582
583
584
585
            "$itemtable.is_deleted" => 0,
            "NOT"                   => [
               "$itemtable.status" => array_merge(
                  $this->getSolvedStatusArray(),
                  $this->getClosedStatusArray()
               )
            ]
         ] + getEntitiesRestrictCriteria($itemtable)
      );
586
   }
yllen's avatar
yllen committed
587

yllen's avatar
CS    
yllen committed
588

589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609


   /**
    * Count active ITIL Objects requested by a user
    *
    * @since 0.83
    *
    * @param integer $users_id ID of the User
    *
    * @return integer
   **/
   function countActiveObjectsForUser($users_id) {
      $linkclass = new $this->userlinkclass();
      return $this->countActiveObjectsFor(
         $linkclass,
         $users_id,
         CommonITILActor::REQUESTER
      );
   }


610
   /**
yllen's avatar
yllen committed
611
    * Count active ITIL Objects assigned to a user
612
    *
Anael Mobilia's avatar
Anael Mobilia committed
613
    * @since 0.83
614
    *
615
    * @param integer $users_id ID of the User
616
617
618
    *
    * @return integer
   **/
yllen's avatar
CS    
yllen committed
619
   function countActiveObjectsForTech($users_id) {
620
      $linkclass = new $this->userlinkclass();
621
622
623
624
      return $this->countActiveObjectsFor(
         $linkclass,
         $users_id,
         CommonITILActor::ASSIGN
625
      );
626
627
   }

yllen's avatar
CS    
yllen committed
628

moyooo's avatar
moyooo committed
629
630
631
   /**
    * Count active ITIL Objects assigned to a group
    *
Anael Mobilia's avatar
Anael Mobilia committed
632
    * @since 0.84
moyooo's avatar
moyooo committed
633
    *
634
    * @param integer $groups_id ID of the User
moyooo's avatar
moyooo committed
635
636
637
638
    *
    * @return integer
   **/
   function countActiveObjectsForTechGroup($groups_id) {
639
      $linkclass = new $this->grouplinkclass();
640
641
642
643
      return $this->countActiveObjectsFor(
         $linkclass,
         $groups_id,
         CommonITILActor::ASSIGN
644
      );
moyooo's avatar
moyooo committed
645
   }
yllen's avatar
CS    
yllen committed
646
647


yllen's avatar
yllen committed
648
649
650
   /**
    * Count active ITIL Objects assigned to a supplier
    *
Anael Mobilia's avatar
Anael Mobilia committed
651
    * @since 0.85
yllen's avatar
yllen committed
652
    *
653
    * @param integer $suppliers_id ID of the Supplier
yllen's avatar
yllen committed
654
655
656
657
    *
    * @return integer
    **/
   function countActiveObjectsForSupplier($suppliers_id) {
658
      $linkclass = new $this->supplierlinkclass();
659
660
661
662
      return $this->countActiveObjectsFor(
         $linkclass,
         $suppliers_id,
         CommonITILActor::ASSIGN
663
      );
yllen's avatar
yllen committed
664
665
666
   }


moyooo's avatar
moyooo committed
667
668
   function cleanDBonPurge() {

669
670
671
672
673
674
675
676
      $link_classes = [
         Itil_Project::class,
         ITILFollowup::class,
         ITILSolution::class
      ];

      if (is_a($this->grouplinkclass, CommonDBConnexity::class, true)) {
         $link_classes[] = $this->grouplinkclass;
moyooo's avatar
moyooo committed
677
      }
yllen's avatar
yllen committed
678

679
680
      if (is_a($this->userlinkclass, CommonDBConnexity::class, true)) {
         $link_classes[] = $this->userlinkclass;
moyooo's avatar
moyooo committed
681
      }
moyooo's avatar
moyooo committed
682

683
684
      if (is_a($this->supplierlinkclass, CommonDBConnexity::class, true)) {
         $link_classes[] = $this->supplierlinkclass;
yllen's avatar
yllen committed
685
      }
686

687
      $this->deleteChildrenAndRelationsFromDb($link_classes);
moyooo's avatar
moyooo committed
688
689
   }

690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
   /**
    * Handle template mandatory fields on update
    *
    * @param array $input Input
    *
    * @return array
    */
   protected function handleTemplateFields(array $input) {
      //// check mandatory fields
      // First get ticket template associated : entity and type/category
      if (isset($input['entities_id'])) {
         $entid = $input['entities_id'];
      } else {
         $entid = $this->fields['entities_id'];
      }

      $type = null;
      if (isset($input['type'])) {
         $type = $input['type'];
      } else if (isset($this->field['type'])) {
         $type = $this->fields['type'];
      }

      if (isset($input['itilcategories_id'])) {
         $categid = $input['itilcategories_id'];
      } else {
         $categid = $this->fields['itilcategories_id'];
      }

      $check_allowed_fields_for_template = false;
      $allowed_fields                    = [];
      if (!Session::isCron()
          && (!Session::haveRight(static::$rightname, UPDATE)
            // Closed tickets
            || in_array($this->fields['status'], $this->getClosedStatusArray()))
         ) {

         $allowed_fields                    = ['id'];
         $check_allowed_fields_for_template = true;

         if (in_array($this->fields['status'], $this->getClosedStatusArray())) {
            $allowed_fields[] = 'status';

            // probably transfer
            $allowed_fields[] = 'entities_id';
            $allowed_fields[] = 'itilcategories_id';
         } else {
            if ($this->canApprove()
                || $this->canAssign()
                || $this->canAssignToMe()
                || isset($input['_from_assignment'])) {
                $allowed_fields[] = 'status';
                $allowed_fields[] = '_accepted';
            }
            // for post-only with validate right or validation created by rules
            $validation_class = static::getType() . 'Validation';
            if (class_exists($validation_class)) {
               if ($validation_class::canValidate($this->fields['id'])
                  || $validation_class::canCreate()
                  || isset($input["_rule_process"])) {
                  $allowed_fields[] = 'global_validation';
               }
            }
            // Manage assign and steal right
            if (static::getType() === Ticket::getType() && Session::haveRightsOr(static::$rightname, [Ticket::ASSIGN, Ticket::STEAL])) {
                $allowed_fields[] = '_itil_assign';
            }

            // Can only update initial fields if no followup or task already added
            if ($this->canUpdateItem()) {
                $allowed_fields[] = 'content';
                $allowed_fields[] = 'urgency';
                $allowed_fields[] = 'priority'; // automatic recalculate if user changes urgence
                $allowed_fields[] = 'itilcategories_id';
                $allowed_fields[] = 'name';
                $allowed_fields[] = 'items_id';
                $allowed_fields[] = '_filename';
                $allowed_fields[] = '_tag_filename';
                $allowed_fields[] = '_prefix_filename';
Thierry Bugier's avatar
Thierry Bugier committed
769
770
771
                $allowed_fields[] = '_content';
                $allowed_fields[] = '_tag_content';
                $allowed_fields[] = '_prefix_content';
772
773
774
775
                $allowed_fields[] = 'takeintoaccount_delay_stat';
            }
         }

776
777
         $ret = [];

778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
         foreach ($allowed_fields as $field) {
            if (isset($input[$field])) {
               $ret[$field] = $input[$field];
            }
         }

         $input = $ret;

         // Only ID return false
         if (count($input) == 1) {
            return false;
         }
      }

      $tt = $this->getITILTemplateToUse(0, $type, $categid, $entid);

      if (count($tt->mandatory)) {
         $mandatory_missing = [];
         $fieldsname        = $tt->getAllowedFieldsNames(true);
         foreach ($tt->mandatory as $key => $val) {
            if ((!$check_allowed_fields_for_template || in_array($key, $allowed_fields))
                && (isset($input[$key])
                    && (empty($input[$key]) || ($input[$key] == 'NULL'))
                )) {
               $mandatory_missing[$key] = $fieldsname[$val];
            }
         }
         if (count($mandatory_missing)) {
            //TRANS: %s are the fields concerned
            $message = sprintf(__('Mandatory fields are not filled. Please correct: %s'),
                               implode(", ", $mandatory_missing));
            Session::addMessageAfterRedirect($message, false, ERROR);
            return false;
         }
      }

      return $input;
   }
yllen's avatar
yllen committed
816

moyooo's avatar
moyooo committed
817
818
819
820
   function prepareInputForUpdate($input) {

      // Add document if needed
      $this->getFromDB($input["id"]); // entities_id field required
821
822
823
824
825
826
827
828
829

      if ($this->getType() !== Ticket::getType()) {
         //cannot be handled here for tickets. @see Ticket::prepareInputForUpdate()
         $input = $this->handleTemplateFields($input);
         if ($input === false) {
            return false;
         }
      }

yllen's avatar
gettext    
yllen committed
830
      if (isset($input["document"]) && ($input["document"] > 0)) {
moyooo's avatar
moyooo committed
831
832
833
         $doc = new Document();
         if ($doc->getFromDB($input["document"])) {
            $docitem = new Document_Item();
834
            if ($docitem->add(['documents_id' => $input["document"],
moyooo's avatar
moyooo committed
835
                                    'itemtype'     => $this->getType(),
836
                                    'items_id'     => $input["id"]])) {
moyooo's avatar
moyooo committed
837
               // Force date_mod of tracking
yllen's avatar
yllen committed
838
               $input["date_mod"]     = $_SESSION["glpi_currenttime"];
moyooo's avatar
moyooo committed
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
               $input['_doc_added'][] = $doc->fields["name"];
            }
         }
         unset($input["document"]);
      }

      if (isset($input["date"]) && empty($input["date"])) {
         unset($input["date"]);
      }

      if (isset($input["closedate"]) && empty($input["closedate"])) {
         unset($input["closedate"]);
      }

      if (isset($input["solvedate"]) && empty($input["solvedate"])) {
         unset($input["solvedate"]);
      }
yllen's avatar
CS    
yllen committed
856

857
858
859
      // "do not compute" flag set by business rules for "takeintoaccount_delay_stat" field
      $do_not_compute_takeintoaccount = $this->isTakeIntoAccountComputationBlocked($input);

moyooo's avatar
moyooo committed
860
861
      if (isset($input['_itil_requester'])) {
         if (isset($input['_itil_requester']['_type'])) {
862
863
864
865
866
867
            $input['_itil_requester'] = [
               'type'                            => CommonITILActor::REQUESTER,
               $this->getForeignKeyField()       => $input['id'],
               '_do_not_compute_takeintoaccount' => $do_not_compute_takeintoaccount,
               '_from_object'                    => true,
            ] + $input['_itil_requester'];
moyooo's avatar
moyooo committed
868

moyooo's avatar
moyooo committed
869
            switch ($input['_itil_requester']['_type']) {
moyooo's avatar
moyooo committed
870
               case "user" :
871
                  if (isset($input['_itil_requester']['use_notification'])
872
                      && is_array($input['_itil_requester']['use_notification'])) {
873
874
                     $input['_itil_requester']['use_notification'] = $input['_itil_requester']['use_notification'][0];
                  }
875
                  if (isset($input['_itil_requester']['alternative_email'])
876
                      && is_array($input['_itil_requester']['alternative_email'])) {
877
878
879
                     $input['_itil_requester']['alternative_email'] = $input['_itil_requester']['alternative_email'][0];
                  }

moyooo's avatar
moyooo committed
880
                  if (!empty($this->userlinkclass)) {
881
882
                     if (isset($input['_itil_requester']['alternative_email'])
                         && $input['_itil_requester']['alternative_email']
Johan Cwiklinski's avatar
Johan Cwiklinski committed
883
                         && !NotificationMailing::isUserAddressValid($input['_itil_requester']['alternative_email'])) {
yllen's avatar
yllen committed
884

885
                        $input['_itil_requester']['alternative_email'] = '';
yllen's avatar
yllen committed
886
                        Session::addMessageAfterRedirect(__('Invalid email address'), false, ERROR);
yllen's avatar
CS    
yllen committed
887
                     }
moyooo's avatar
moyooo committed
888

889
890
                     if ((isset($input['_itil_requester']['alternative_email'])
                          && $input['_itil_requester']['alternative_email'])
yllen's avatar
gettext    
yllen committed
891
                         || ($input['_itil_requester']['users_id'] > 0)) {
yllen's avatar
yllen committed
892

893
                        $useractors = new $this->userlinkclass();
894
                        if (isset($input['_auto_update'])
moyooo's avatar
moyooo committed
895
                            || $useractors->can(-1, CREATE, $input['_itil_requester'])) {
896
                           $useractors->add($input['_itil_requester']);
yllen's avatar
CS    
yllen committed
897
                           $input['_forcenotif']                     = true;
898
                        }
moyooo's avatar
moyooo committed
899
900
901
902
903
                     }
                  }
                  break;

               case "group" :
yllen's avatar
gettext    
yllen committed
904
905
                  if (!empty($this->grouplinkclass)
                      && ($input['_itil_requester']['groups_id'] > 0)) {
moyooo's avatar
moyooo committed
906
                     $groupactors = new $this->grouplinkclass();
907
                     if (isset($input['_auto_update'])
moyooo's avatar
moyooo committed
908
                         || $groupactors->can(-1, CREATE, $input['_itil_requester'])) {
moyooo's avatar
moyooo committed
909
                        $groupactors->add($input['_itil_requester']);
yllen's avatar
CS    
yllen committed
910
                        $input['_forcenotif']                     = true;
moyooo's avatar
moyooo committed
911
912
913
914
915
916
917
                     }
                  }
                  break;
            }
         }
      }

moyooo's avatar
moyooo committed
918
919
      if (isset($input['_itil_observer'])) {
         if (isset($input['_itil_observer']['_type'])) {
920
921
922
923
924
925
            $input['_itil_observer'] = [
               'type'                            => CommonITILActor::OBSERVER,
               $this->getForeignKeyField()       => $input['id'],
               '_do_not_compute_takeintoaccount' => $do_not_compute_takeintoaccount,
               '_from_object'                    => true,
            ] + $input['_itil_observer'];
moyooo's avatar
moyooo committed
926

moyooo's avatar
moyooo committed
927
            switch ($input['_itil_observer']['_type']) {
moyooo's avatar
moyooo committed
928
               case "user" :
929
                  if (isset($input['_itil_observer']['use_notification'])
930
                      && is_array($input['_itil_observer']['use_notification'])) {
931
932
                     $input['_itil_observer']['use_notification'] = $input['_itil_observer']['use_notification'][0];
                  }
933
                  if (isset($input['_itil_observer']['alternative_email'])
934
                      && is_array($input['_itil_observer']['alternative_email'])) {
935
936
937
                     $input['_itil_observer']['alternative_email'] = $input['_itil_observer']['alternative_email'][0];
                  }

moyooo's avatar
moyooo committed
938
                  if (!empty($this->userlinkclass)) {
939
940
                     if (isset($input['_itil_observer']['alternative_email'])
                         && $input['_itil_observer']['alternative_email']
Johan Cwiklinski's avatar
Johan Cwiklinski committed
941
                         && !NotificationMailing::isUserAddressValid($input['_itil_observer']['alternative_email'])) {
yllen's avatar
yllen committed
942

943
                        $input['_itil_observer']['alternative_email'] = '';
yllen's avatar
yllen committed
944
                        Session::addMessageAfterRedirect(__('Invalid email address'), false, ERROR);
945
946
947
                     }
                     if ((isset($input['_itil_observer']['alternative_email'])
                          && $input['_itil_observer']['alternative_email'])
yllen's avatar
gettext    
yllen committed
948
                         || ($input['_itil_observer']['users_id'] > 0)) {
949
                        $useractors = new $this->userlinkclass();
950
                        if (isset($input['_auto_update'])
moyooo's avatar
moyooo committed
951
                           || $useractors->can(-1, CREATE, $input['_itil_observer'])) {
952
                           $useractors->add($input['_itil_observer']);
yllen's avatar
CS    
yllen committed
953
                           $input['_forcenotif']                    = true;
954
                        }
moyooo's avatar
moyooo committed
955
956
957
958
959
                     }
                  }
                  break;

               case "group" :
960
                  if (!empty($this->grouplinkclass)
yllen's avatar
gettext    
yllen committed
961
                       && ($input['_itil_observer']['groups_id'] > 0)) {
moyooo's avatar
moyooo committed
962
                     $groupactors = new $this->grouplinkclass();
963
                     if (isset($input['_auto_update'])
moyooo's avatar
moyooo committed
964
                         || $groupactors->can(-1, CREATE, $input['_itil_observer'])) {
moyooo's avatar
moyooo committed
965
                        $groupactors->add($input['_itil_observer']);
yllen's avatar
CS    
yllen committed
966
                        $input['_forcenotif']                    = true;
moyooo's avatar
moyooo committed
967
968
969
970
971
972
973
                     }
                  }
                  break;
            }
         }
      }

moyooo's avatar
moyooo committed
974
975
      if (isset($input['_itil_assign'])) {
         if (isset($input['_itil_assign']['_type'])) {
976
977
978
979
980
981
            $input['_itil_assign'] = [
               'type'                            => CommonITILActor::ASSIGN,
               $this->getForeignKeyField()       => $input['id'],
               '_do_not_compute_takeintoaccount' => $do_not_compute_takeintoaccount,
               '_from_object'                    => true,
            ] + $input['_itil_assign'];
moyooo's avatar
moyooo committed
982

yllen's avatar
yllen committed
983
984
985
986
987
988
989
990
991
            if (isset($input['_itil_assign']['use_notification'])
                  && is_array($input['_itil_assign']['use_notification'])) {
               $input['_itil_assign']['use_notification'] = $input['_itil_assign']['use_notification'][0];
            }
            if (isset($input['_itil_assign']['alternative_email'])
                  && is_array($input['_itil_assign']['alternative_email'])) {
               $input['_itil_assign']['alternative_email'] = $input['_itil_assign']['alternative_email'][0];
            }

moyooo's avatar
moyooo committed
992
            switch ($input['_itil_assign']['_type']) {
moyooo's avatar
moyooo committed
993
               case "user" :
yllen's avatar
gettext    
yllen committed
994
                  if (!empty($this->userlinkclass)
995
996
997
                      && ((isset($input['_itil_assign']['alternative_email'])
                           && $input['_itil_assign']['alternative_email'])
                          || $input['_itil_assign']['users_id'] > 0)) {
moyooo's avatar
moyooo committed
998
                     $useractors = new $this->userlinkclass();
999
                     if (isset($input['_auto_update'])
moyooo's avatar
moyooo committed
1000
                         || $useractors->can(-1, CREATE, $input['_itil_assign'])) {
moyooo's avatar
moyooo committed
1001
                        $useractors->add($input['_itil_assign']);
yllen's avatar
CS    
yllen committed
1002
                        $input['_forcenotif']                  = true;
1003
                        if (((!isset($input['status'])
yllen's avatar
CS    
yllen committed
1004
1005
                             && in_array($this->fields['status'], $this->getNewStatusArray()))
                            || (isset($input['status'])
1006
1007
                                && in_array($input['status'], $this->getNewStatusArray())))
                            && !$this->isStatusComputationBlocked($input)) {
yllen's avatar
yllen committed
1008
                           if (in_array(self::ASSIGNED, array_keys($this->getAllStatusArray()))) {
yllen's avatar
yllen committed
1009
1010
                              $input['status'] = self::ASSIGNED;
                           }
moyooo's avatar
moyooo committed
1011
1012
1013
1014
1015
1016
                        }
                     }
                  }
                  break;

               case "group" :
yllen's avatar
gettext    
yllen committed
1017
1018
                  if (!empty($this->grouplinkclass)
                      && ($input['_itil_assign']['groups_id'] > 0)) {
moyooo's avatar
moyooo committed
1019
1020
                     $groupactors = new $this->grouplinkclass();

1021
                     if (isset($input['_auto_update'])
moyooo's avatar
moyooo committed
1022
                         || $groupactors->can(-1, CREATE, $input['_itil_assign'])) {
moyooo's avatar
moyooo committed
1023
                        $groupactors->add($input['_itil_assign']);
yllen's avatar
CS    
yllen committed
1024
                        $input['_forcenotif']                  = true;
1025
                        if (((!isset($input['status'])
yllen's avatar
CS    
yllen committed
1026
1027
                             && (in_array($this->fields['status'], $this->getNewStatusArray())))
                            || (isset($input['status'])
1028
1029
                                && (in_array($input['status'], $this->getNewStatusArray()))))
                            && !$this->isStatusComputationBlocked($input)) {
yllen's avatar
yllen committed
1030
                           if (in_array(self::ASSIGNED, array_keys($this->getAllStatusArray()))) {
yllen's avatar
yllen committed
1031
1032
                              $input['status'] = self::ASSIGNED;
                           }
moyooo's avatar
moyooo committed
1033
1034
1035
1036
                        }
                     }
                  }
                  break;
yllen's avatar
CS    
yllen committed
1037

1038
1039
               case "supplier" :
                  if (!empty($this->supplierlinkclass)
1040
1041
1042
                      && ((isset($input['_itil_assign']['alternative_email'])
                           && $input['_itil_assign']['alternative_email'])
                          || $input['_itil_assign']['suppliers_id'] > 0)) {
1043
1044
                     $supplieractors = new $this->supplierlinkclass();
                     if (isset($input['_auto_update'])
moyooo's avatar
moyooo committed
1045
                         || $supplieractors->can(-1, CREATE, $input['_itil_assign'])) {
1046
                        $supplieractors->add($input['_itil_assign']);
yllen's avatar
CS    
yllen committed
1047
                        $input['_forcenotif']                  = true;
1048
                        if (((!isset($input['status'])
yllen's avatar
CS    
yllen committed
1049
1050
                             && (in_array($this->fields['status'], $this->getNewStatusArray())))
                            || (isset($input['status'])
1051
1052
                                && (in_array($input['status'], $this->getNewStatusArray()))))
                            && !$this->isStatusComputationBlocked($input)) {
yllen's avatar
yllen committed
1053
                           if (in_array(self::ASSIGNED, array_keys($this->getAllStatusArray()))) {
yllen's avatar
yllen committed
1054
1055
1056
                              $input['status'] = self::ASSIGNED;
                           }

1057
1058
1059
                        }
                     }
                  }
yllen's avatar
CS    
yllen committed
1060
                  break;
moyooo's avatar
moyooo committed
1061
1062
1063
            }
         }
      }
yllen's avatar
yllen committed
1064

1065
      $this->addAdditionalActors($input);
yllen's avatar
yllen committed
1066

remi's avatar
remi committed
1067
      // set last updater if interactive user
1068
1069
      if (!Session::isCron()) {
         $input['users_id_lastupdater'] = Session::getLoginUserID();
moyooo's avatar
moyooo committed
1070
1071
      }

1072
1073
1074
1075
1076
      $solvedclosed = array_merge(
         $this->getSolvedStatusArray(),
         $this->getClosedStatusArray()
      );

1077
      if (isset($input["status"])
1078
          && !in_array($input["status"], $solvedclosed)) {
moyooo's avatar
moyooo committed
1079
         $input['solvedate'] = 'NULL';
remi's avatar
remi committed
1080
      }
moyooo's avatar
moyooo committed
1081

1082
      if (isset($input["status"]) && !in_array($input["status"], $this->getClosedStatusArray())) {
moyooo's avatar
moyooo committed
1083
         $input['closedate'] = 'NULL';
remi's avatar
remi committed
1084
      }
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099

      // Setting a solution type means the ticket is solved
      if (isset($input["solutiontypes_id"])
          && (!isset($input['status']) || !in_array($input["status"], $solvedclosed))) {
         $solution = new ITILSolution();
         $soltype = new SolutionType();
         $soltype->getFromDB($input['solutiontypes_id']);
         $solution->add([
            'itemtype'           => $this->getType(),
            'items_id'           => $this->getID(),
            'solutiontypes_id'   => $input['solutiontypes_id'],
            'content'            => 'Solved using type ' . $soltype->getName()
         ]);
      }

moyooo's avatar
moyooo committed
1100
1101
1102
      return $input;
   }

1103
   function post_updateItem($history = 1) {
Thierry Bugier's avatar
Thierry Bugier committed
1104
1105
1106
1107
      // Handle files pasted in the file field
      $this->input = $this->addFiles($this->input);

      // Handle files pasted in the text area
1108
      if (!isset($this->input['_donotadddocs']) || !$this->input['_donotadddocs']) {
Thierry Bugier's avatar
Thierry Bugier committed
1109
1110
1111
1112
1113
         $options = [
            'force_update' => true,
            'name' => 'content',
            'content_field' => 'content',
         ];
1114
         if (isset($this->input['solution'])) {
Thierry Bugier's avatar
Thierry Bugier committed
1115
            $options['name'] = 'solution';
1116
1117
1118
1119
1120
1121
            $options['content_field'] = 'solution';
         }
         $this->input = $