Issues (1844)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  Header Injection
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

class/Ticket.php (13 issues)

1
<?php declare(strict_types=1);
2
3
namespace XoopsModules\Xhelp;
4
5
/*
6
 * You may not change or alter any portion of this comment or credits
7
 * of supporting developers from this source code or any supporting source code
8
 * which is considered copyrighted (c) material of the original comment or credit authors.
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.
13
 */
14
15
/**
16
 * @copyright    {@link https://xoops.org/ XOOPS Project}
17
 * @license      {@link https://www.gnu.org/licenses/gpl-2.0.html GNU GPL 2 or later}
18
 * @author       Eric Juden <[email protected]>
19
 * @author       XOOPS Development Team
20
 */
21
22
use function md5;
23
24
if (!\defined('XHELP_CLASS_PATH')) {
25
    exit();
26
}
27
// require_once XHELP_CLASS_PATH . '/BaseObjectHandler.php';
28
$path = \dirname(__DIR__, 3);
29
require_once $path . '/mainfile.php';
30
//require_once $path . '/include/cp_functions.php';
31
//require_once $path . '/include/cp_header.php';
32
33
global $xoopsUser;
34
35
/**
36
 * Ticket class
37
 *
38
 * Information about an individual ticket
39
 *
40
 * <code>
41
 * $ticketHandler = $this->helper->getHandler('Ticket');
42
 * $ticket = $ticketHandler->get(1);
43
 * $ticket_id = $ticket->getVar('id');
44
 * $responses = $ticket->getResponses();
45
 * echo $ticket->lastUpdated();
46
 * </code>
47
 *
48
 * @author  Eric Juden <[email protected]>
49
 */
50
class Ticket extends \XoopsObject
51
{
52
    private $helper;
53
54
    /**
55
     * Ticket constructor.
56
     * @param int|array|null $id
57
     */
58
    public function __construct($id = null)
59
    {
60
        $this->initVar('id', \XOBJ_DTYPE_INT, null, false);
61
        $this->initVar('uid', \XOBJ_DTYPE_INT, null, false);                      // will store Xoops user id
62
        $this->initVar('subject', \XOBJ_DTYPE_TXTBOX, null, true, 100);
63
        $this->initVar('description', \XOBJ_DTYPE_TXTAREA, null, false, 1000000);
64
        $this->initVar('department', \XOBJ_DTYPE_INT, null, false);
65
        $this->initVar('priority', \XOBJ_DTYPE_INT, null, false);
66
        $this->initVar('status', \XOBJ_DTYPE_INT, null, false);
67
        $this->initVar('lastUpdated', \XOBJ_DTYPE_INT, null, false);
68
        $this->initVar('posted', \XOBJ_DTYPE_INT, null, false);
69
        $this->initVar('ownership', \XOBJ_DTYPE_INT, null, false);                // will store Xoops user id
70
        $this->initVar('closedBy', \XOBJ_DTYPE_INT, null, false);                 // will store Xoops user id
71
        $this->initVar('totalTimeSpent', \XOBJ_DTYPE_INT, null, false);
72
        $this->initVar('userIP', \XOBJ_DTYPE_TXTBOX, null, false, 25);
73
        $this->initVar('elapsed', \XOBJ_DTYPE_INT, null, false);
74
        $this->initVar('lastUpdate', \XOBJ_DTYPE_INT, null, false);
75
        $this->initVar('emailHash', \XOBJ_DTYPE_TXTBOX, '', true, 100);
76
        $this->initVar('email', \XOBJ_DTYPE_TXTBOX, '', true, 100);
77
        $this->initVar('serverid', \XOBJ_DTYPE_INT, null, false);                 //will store email server this was picked up from
78
        $this->initVar('overdueTime', \XOBJ_DTYPE_INT, null, false);
79
80
        $this->helper = Helper::getInstance();
81
82
        if (null !== $id) {
83
            if (\is_array($id)) {
84
                $this->assignVars($id);
85
            }
86
        } else {
87
            $this->setNew();
88
        }
89
    }
90
91
    /**
92
     * retrieve the department object associated with this ticket
93
     *
94
     * @return Department|bool {@link Department}
95
     */
96
    public function getDepartment()
97
    {
98
        $departmentHandler = $this->helper->getHandler('Department');
99
100
        return $departmentHandler->get($this->getVar('department'));
101
    }
102
103
    /**
104
     * create an md5 hash based on the ID and emailaddress. Use this as a lookup key when trying to find a ticket.
105
     *
106
     * @param string $email
107
     */
108
    public function createEmailHash(string $email): void
109
    {
110
        if ('' === $this->getVar('posted')) {
111
            $this->setVar('posted', \time());
112
        }
113
        $hash = $this->getVar('posted') . '-' . $email;
114
        $hash = md5($hash);
115
116
        $this->setVar('email', $email);
117
        $this->setVar('emailHash', $hash);
118
    }
119
120
    /**
121
     * retrieve all emails attached to this ticket object
122
     * @param bool $activeOnly
123
     * @return array of <a href='psi_element://TicketEmail'>TicketEmail</a> objects
124
     * objects
125
     */
126
    public function getEmails(bool $activeOnly = false): array
127
    {
128
        $arr = [];
129
        $id  = (int)$this->getVar('id');
130
        if (!$id) {
131
            return $arr;
132
        }
133
134
        $hEmails  = $this->helper->getHandler('TicketEmails');
135
        $criteria = new \CriteriaCompo(new \Criteria('ticketid', $id));
136
        if ($activeOnly) {
137
            $criteria->add(new \Criteria('suppress', 0));
138
        }
139
        $arr = $hEmails->getObjects($criteria);
140
141
        return $arr;
142
    }
143
144
    /**
145
     * retrieve all files attached to this ticket object
146
     *
147
     * @return array of {@link File} objects
148
     */
149
    public function getFiles(): array
150
    {
151
        $arr = [];
152
        $id  = (int)$this->getVar('id');
153
        if (!$id) {
154
            return $arr;
155
        }
156
157
        $fileHandler = $this->helper->getHandler('File');
158
        $criteria    = new \CriteriaCompo(new \Criteria('ticketid', $id));
159
        $criteria->setSort('responseid');
160
        $arr = $fileHandler->getObjects($criteria);
161
162
        return $arr;
163
    }
164
165
    /**
166
     * retrieve all responses attached to this ticket object
167
     *
168
     * @param int $limit
169
     * @param int $start
170
     * @return array of <a href='psi_element://Response'>Response</a> objects
171
     * objects
172
     */
173
    public function getResponses(int $limit = 0, int $start = 0): array
174
    {
175
        $arr = [];
176
        $id  = (int)$this->getVar('id');
177
        if (!$id) {
178
            return $arr;
179
        }
180
        $responseHandler = $this->helper->getHandler('Response');
181
        $criteria        = new \CriteriaCompo(new \Criteria('ticketid', $id));
182
        $criteria->setSort('updateTime');
183
        $criteria->setOrder('DESC');
184
        $criteria->setLimit($limit);
185
        $criteria->setStart($start);
186
187
        $arr = $responseHandler->getObjects($criteria);
188
189
        return $arr;
190
    }
191
192
    /**
193
     * Retrieve number of responses for this ticket object
194
     * @return int Number of Responses
195
     */
196
    public function getResponseCount(): int
197
    {
198
        $responseHandler = $this->helper->getHandler('Response');
199
        $criteria        = new \Criteria('ticketid', $this->getVar('id'));
0 ignored issues
show
It seems like $this->getVar('id') can also be of type array and array; however, parameter $value of Criteria::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

199
        $criteria        = new \Criteria('ticketid', /** @scrutinizer ignore-type */ $this->getVar('id'));
Loading history...
200
201
        return $responseHandler->getCount($criteria);
202
    }
203
204
    /**
205
     *  Get all reviews for the current ticket
206
     * @param int $limit
207
     * @param int $start
208
     * @return array of <a href='psi_element://StaffReview'>StaffReview</a>
209
     */
210
    public function getReviews(int $limit = 0, int $start = 0): array
211
    {
212
        $helper = Helper::getInstance();
213
        $arr    = [];
214
        $id     = (int)$this->getVar('id');
215
        if (!$id) {
216
            return $arr;
217
        }
218
        /** @var \XoopsModules\Xhelp\StaffReviewHandler $staffReviewHandler */
219
        $staffReviewHandler = $helper->getHandler('StaffReview');
220
        $criteria           = new \CriteriaCompo(new \Criteria('ticketid', $id));
221
        $criteria->setSort('responseid');
222
        $criteria->setOrder('DESC');
223
        $criteria->setLimit($limit);
224
        $criteria->setStart($start);
225
226
        $arr = $staffReviewHandler->getObjects($criteria);
227
228
        return $arr;
229
    }
230
231
    /**
232
     * retrieve all log messages attached to this ticket object
233
     *
234
     * @param int $limit
235
     * @param int $start
236
     * @return array of <a href='psi_element://LogMessages'>LogMessages</a> objects
237
     * objects
238
     */
239
    public function getLogs(int $limit = 0, int $start = 0): array
240
    {
241
        $arr = [];
242
        $id  = (int)$this->getVar('id');
243
        if (!$id) {
244
            return $arr;
245
        }
246
        /** @var \XoopsModules\Xhelp\LogMessageHandler $this- >logmessageHandler */
247
        $logMessageHandler = $this->helper->getHandler('LogMessage');
248
        $criteria          = new \CriteriaCompo(new \Criteria('ticketid', $id));
249
        $criteria->setSort('lastUpdated');
250
        $criteria->setOrder('DESC');
251
        $criteria->setLimit($limit);
252
        $criteria->setStart($start);
253
254
        $arr = $logMessageHandler->getObjects($criteria);
255
256
        return $arr;
257
    }
258
259
    /**
260
     * @param string                                   $post_field
261
     * @param \XoopsModules\Xhelp\Response|string|null $response
262
     * @param array|string|null                        $allowed_mimetypes
263
     * @return \XoopsModules\Xhelp\File|array|false|string|void
264
     */
265
    public function storeUpload(string $post_field, $response = null, $allowed_mimetypes = null)
266
    {
267
        global $xoopsUser, $xoopsDB, $xoopsModule;
268
        // require_once XHELP_CLASS_PATH . '/uploader.php';
269
270
        $config = Utility::getModuleConfig();
271
272
        $ticketid = $this->getVar('id');
273
274
        if (null === $allowed_mimetypes) {
275
            $mimetypeHandler   = $this->helper->getHandler('Mimetype');
276
            $allowed_mimetypes = $mimetypeHandler->checkMimeTypes($post_field);
277
            if (!$allowed_mimetypes) {
0 ignored issues
show
The condition $allowed_mimetypes is always false.
Loading history...
278
                return false;
279
            }
280
        }
281
282
        $maxfilesize   = (int)$config['xhelp_uploadSize'];
283
        $maxfilewidth  = (int)$config['xhelp_uploadWidth'];
284
        $maxfileheight = (int)$config['xhelp_uploadHeight'];
285
        if (!\is_dir(XHELP_UPLOAD_PATH)) {
0 ignored issues
show
The constant XoopsModules\Xhelp\XHELP_UPLOAD_PATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
286
            if (!\mkdir($concurrentDirectory = XHELP_UPLOAD_PATH, 0757) && !\is_dir($concurrentDirectory)) {
287
                throw new \RuntimeException(\sprintf('Directory "%s" was not created', $concurrentDirectory));
288
            }
289
        }
290
291
        $uploader = new MediaUploader(XHELP_UPLOAD_PATH . '/', $allowed_mimetypes, $maxfilesize, $maxfilewidth, $maxfileheight);
0 ignored issues
show
It seems like $allowed_mimetypes can also be of type string; however, parameter $allowedMimeTypes of XoopsModules\Xhelp\MediaUploader::__construct() does only seem to accept array|integer, maybe add an additional type check? ( Ignorable by Annotation )

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

291
        $uploader = new MediaUploader(XHELP_UPLOAD_PATH . '/', /** @scrutinizer ignore-type */ $allowed_mimetypes, $maxfilesize, $maxfilewidth, $maxfileheight);
Loading history...
292
        if ($uploader->fetchMedia($post_field)) {
293
            if (null === $response) {
294
                $uploader->setTargetFileName($ticketid . '_' . $uploader->getMediaName());
295
            } else {
296
                if ($response > 0) {
297
                    $uploader->setTargetFileName($ticketid . '_' . $response . '_' . $uploader->getMediaName());
0 ignored issues
show
Are you sure $response of type XoopsModules\Xhelp\Response|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

297
                    $uploader->setTargetFileName($ticketid . '_' . /** @scrutinizer ignore-type */ $response . '_' . $uploader->getMediaName());
Loading history...
298
                } else {
299
                    $uploader->setTargetFileName($ticketid . '_' . $uploader->getMediaName());
300
                }
301
            }
302
            if ($uploader->upload()) {
303
                $fileHandler = $this->helper->getHandler('File');
304
                $file        = $fileHandler->create();
305
                $file->setVar('filename', $uploader->getSavedFileName());
306
                $file->setVar('ticketid', $ticketid);
307
                $file->setVar('mimetype', $allowed_mimetypes);
308
                $file->setVar('responseid', (null !== $response ? (int)$response : 0));
309
310
                if ($fileHandler->insert($file)) {
311
                    return $file;
312
                }
313
314
                return $uploader->getErrors();
315
            }
316
317
            return $uploader->getErrors();
318
        }
319
    }
320
321
    /**
322
     * @param string     $post_field
323
     * @param array|null $allowed_mimetypes
324
     * @param array      $errors
325
     * @return bool
326
     */
327
    public function checkUpload(string $post_field, ?array &$allowed_mimetypes, array &$errors): bool
328
    {
329
        // require_once XHELP_CLASS_PATH . '/uploader.php';
330
        $config = Utility::getModuleConfig();
331
332
        $maxfilesize   = (int)$config['xhelp_uploadSize'];
333
        $maxfilewidth  = (int)$config['xhelp_uploadWidth'];
334
        $maxfileheight = (int)$config['xhelp_uploadHeight'];
335
        $errors        = [];
336
337
        if (null === $allowed_mimetypes) {
0 ignored issues
show
The condition null === $allowed_mimetypes is always false.
Loading history...
338
            $mimetypeHandler   = $this->helper->getHandler('Mimetype');
339
            $allowed_mimetypes = $mimetypeHandler->checkMimeTypes($post_field);
340
            if (!$allowed_mimetypes) {
341
                $errors[] = \_XHELP_MESSAGE_WRONG_MIMETYPE;
342
343
                return false;
344
            }
345
        }
346
        $uploader = new MediaUploader(XHELP_UPLOAD_PATH . '/', $allowed_mimetypes, $maxfilesize, $maxfilewidth, $maxfileheight);
0 ignored issues
show
The constant XoopsModules\Xhelp\XHELP_UPLOAD_PATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
347
348
        if ($uploader->fetchMedia($post_field)) {
349
            return true;
350
        }
351
352
        $errors = \array_merge($errors, $uploader->getErrors(false));
353
354
        return false;
355
    }
356
357
    /**
358
     * determine last time the ticket was updated relative to the current user
359
     *
360
     * @param string $format
361
     * @return int Timestamp of last update
362
     */
363
    public function lastUpdated(string $format = 'l'): int
364
    {
365
        return (int)\formatTimestamp($this->getVar('lastUpdated'), $format);
366
    }
367
368
    /**
369
     * @param string $format
370
     * @return string
371
     */
372
    public function posted(string $format = 'l'): string
373
    {
374
        return \formatTimestamp($this->getVar('posted'), $format);
375
    }
376
377
    /**
378
     * return a simplified measurement of elapsed ticket time
379
     *
380
     * @return string Elapsed time
381
     */
382
    public function elapsed(): string
383
    {
384
        $tmp = Utility::getElapsedTime($this->getVar('elapsed'));
385
386
        return $this->prettyElapsed($tmp);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->prettyElapsed($tmp) could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
387
    }
388
389
    /**
390
     * @return string
391
     */
392
    public function lastUpdate(): string
393
    {
394
        $tmp = Utility::getElapsedTime($this->getVar('lastUpdate'));
395
396
        return $this->prettyElapsed($tmp);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->prettyElapsed($tmp) could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
397
    }
398
399
    /**
400
     * @param array $time
401
     * @return string
402
     */
403
    private function prettyElapsed(array $time): ?string
404
    {
405
        $useSingle = false;
406
407
        foreach ($time as $unit => $value) {
408
            if ($value) {
409
                if (1 == $value) {
410
                    $useSingle = true;
411
                }
412
                switch ($unit) {
413
                    case 'years':
414
                        $unit_dsc = ($useSingle ? \_XHELP_TIME_YEAR : \_XHELP_TIME_YEARS);
415
                        break;
416
                    case 'weeks':
417
                        $unit_dsc = ($useSingle ? \_XHELP_TIME_WEEK : \_XHELP_TIME_WEEKS);
418
                        break;
419
                    case 'days':
420
                        $unit_dsc = ($useSingle ? \_XHELP_TIME_DAY : \_XHELP_TIME_DAYS);
421
                        break;
422
                    case 'hours':
423
                        $unit_dsc = ($useSingle ? \_XHELP_TIME_HOUR : \_XHELP_TIME_HOURS);
424
                        break;
425
                    case 'minutes':
426
                        $unit_dsc = ($useSingle ? \_XHELP_TIME_MIN : \_XHELP_TIME_MINS);
427
                        break;
428
                    case 'seconds':
429
                        $unit_dsc = ($useSingle ? \_XHELP_TIME_SEC : \_XHELP_TIME_SECS);
430
                        break;
431
                    default:
432
                        $unit_dsc = $unit;
433
                        break;
434
                }
435
436
                return "$value $unit_dsc";
437
            }
438
        }
439
    }
440
441
    /**
442
     * Determine if ticket is overdue
443
     *
444
     * @return bool
445
     */
446
    public function isOverdue(): bool
447
    {
448
        $config        = Utility::getModuleConfig();
449
        $statusHandler = $this->helper->getHandler('Status');
450
        if (isset($config['xhelp_overdueTime'])) {
451
            $overdueTime = $config['xhelp_overdueTime'];
452
453
            if ($overdueTime) {
454
                $status = $statusHandler->get($this->getVar('status'));
455
                if (1 == $status->getVar('state')) {
456
                    if (\time() > $this->getVar('overdueTime')) {
457
                        return true;
458
                    }
459
                }
460
            }
461
        }
462
463
        return false;
464
    }
465
466
    /**
467
     * @param string $email
468
     * @param int    $uid
469
     * @param int    $suppress
470
     * @return bool
471
     */
472
    public function addSubmitter(string $email, int $uid, int $suppress = 0): bool
473
    {
474
        $uid = $uid;
475
476
        if ('' != $email) {
477
            $ticketEmailsHandler = $this->helper->getHandler('TicketEmails');
478
            $tEmail              = $ticketEmailsHandler->create();
479
480
            $tEmail->setVar('ticketid', $this->getVar('id'));
481
            $tEmail->setVar('email', $email);
482
            $tEmail->setVar('uid', $uid);
483
            $tEmail->setVar('suppress', $suppress);
484
485
            if ($ticketEmailsHandler->insert($tEmail)) {
486
                return true;
487
            }
488
        }
489
490
        return false;
491
    }
492
493
    /**
494
     * @param int $ticket2_id
495
     * @return bool|mixed
496
     */
497
    public function merge(int $ticket2_id)
498
    {
499
        global $xoopsDB;
500
        $ticket2_id = $ticket2_id;
501
502
        // Retrieve $ticket2
503
        $ticketHandler = $this->helper->getHandler('Ticket');
504
        $mergeTicket   = $ticketHandler->get($ticket2_id);
505
506
        // Figure out which ticket is older
507
        if ($this->getVar('posted') < $mergeTicket->getVar('posted')) {   // If this ticket is older than the 2nd ticket
508
            $keepTicket = $this;
509
            $loseTicket = $mergeTicket;
510
        } else {
511
            $keepTicket = $mergeTicket;
512
            $loseTicket = $this;
513
        }
514
515
        $keep_id = $keepTicket->getVar('id');
516
        $lose_id = $loseTicket->getVar('id');
517
518
        // Copy ticket subject and description of 2nd ticket as response to $this ticket
519
        $responseid = $keepTicket->addResponse($loseTicket->getVar('uid'), $keep_id, $loseTicket->getVar('subject', 'e') . ' - ' . $loseTicket->getVar('description', 'e'), $loseTicket->getVar('posted'), $loseTicket->getVar('userIP'));
520
521
        // Copy 2nd ticket file attachments to $this ticket
522
        $fileHandler = $this->helper->getHandler('File');
523
        $criteria    = new \Criteria('ticketid', $lose_id);
0 ignored issues
show
It seems like $lose_id can also be of type array and array; however, parameter $value of Criteria::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

523
        $criteria    = new \Criteria('ticketid', /** @scrutinizer ignore-type */ $lose_id);
Loading history...
524
        $files       = $fileHandler->getObjects($criteria);
525
        foreach ($files as $file) {
526
            $file->rename($keep_id, $responseid);
527
        }
528
        $success = $fileHandler->updateAll('ticketid', $keep_id, $criteria);
0 ignored issues
show
The assignment to $success is dead and can be removed.
Loading history...
529
530
        // Copy 2nd ticket responses as responses to $this ticket
531
        $responseHandler = $this->helper->getHandler('Response');
532
        $criteria        = new \Criteria('ticketid', $lose_id);
533
        $success         = $responseHandler->updateAll('ticketid', $keep_id, $criteria);
534
535
        // Change file responseid to match the response added to merged ticket
536
        $criteria = new \CriteriaCompo(new \Criteria('ticketid', $lose_id));
537
        $criteria->add(new \Criteria('responseid', 0));
538
        $success = $fileHandler->updateAll('responseid', $responseid, $criteria);
539
540
        // Add 2nd ticket submitter to $this ticket via ticketEmails table
541
        $ticketEmailsHandler = $this->helper->getHandler('TicketEmails');
542
        $criteria            = new \Criteria('ticketid', $lose_id);
543
        $success             = $ticketEmailsHandler->updateAll('ticketid', $keep_id, $criteria);
544
545
        // Remove $loseTicket
546
        $criteria = new \Criteria('id', $lose_id);
547
        if (!$ticketHandler->deleteAll($criteria)) {
548
            return false;
549
        }
550
551
        return $keep_id;
552
    }
553
554
    /**
555
     * Check if the supplied user can add a response to the ticket
556
     * @param \XoopsUser $xoopsUser The user to check
557
     * @return bool
558
     */
559
    public function canAddResponse(\XoopsUser $xoopsUser): bool
560
    {
561
        //1. If the $xoopsUser a valid \XoopsUser Object
562
        if (!$xoopsUser instanceof \XoopsUser) {
0 ignored issues
show
$xoopsUser is always a sub-type of XoopsUser.
Loading history...
563
            return false;
564
        }
565
566
        //2. Is the user one of the "ticket submitters"
567
        $ticketEmailsHandler = $this->helper->getHandler('TicketEmails');
568
        $criteria            = new \CriteriaCompo(new \Criteria('ticketid', $this->getVar('id')));
0 ignored issues
show
It seems like $this->getVar('id') can also be of type array and array; however, parameter $value of Criteria::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

568
        $criteria            = new \CriteriaCompo(new \Criteria('ticketid', /** @scrutinizer ignore-type */ $this->getVar('id')));
Loading history...
569
        $criteria->add(new \Criteria('uid', $xoopsUser->getVar('uid')));
570
        $count = $ticketEmailsHandler->getCount($criteria);
571
572
        if ($count > 0) {
573
            return true;
574
        }
575
576
        //3. Is the user a staff member?
577
        global $xhelp_isStaff, $staff;
578
        if ($xhelp_isStaff) {
579
            if ($staff->checkRoleRights(\XHELP_SEC_RESPONSE_ADD, $this->getVar('department'))) {
580
                return true;
581
            }
582
        }
583
584
        //4. If neither option is true, user cannot add response.
585
        return false;
586
    }
587
588
    /**
589
     * @param int    $uid
590
     * @param int    $ticketid
591
     * @param string $message
592
     * @param int    $updateTime
593
     * @param string $userIP
594
     * @param int    $private
595
     * @param int    $timeSpent
596
     * @param bool   $ret_obj
597
     * @return bool|mixed|\XoopsObject
598
     */
599
    public function addResponse(
600
        int $uid, int $ticketid, string $message, int $updateTime, string $userIP, int $private = 0, int $timeSpent = 0, bool $ret_obj = false
601
    ) {
602
        $uid        = $uid;
603
        $ticketid   = $ticketid;
604
        $updateTime = $updateTime;
605
        $private    = $private;
606
        $timeSpent  = $timeSpent;
607
608
        $responseHandler = $this->helper->getHandler('Response');
609
        $newResponse     = $responseHandler->create();
610
        $newResponse->setVar('uid', $uid);
611
        $newResponse->setVar('ticketid', $ticketid);
612
        $newResponse->setVar('message', $message);
613
        $newResponse->setVar('timeSpent', $timeSpent);
614
        $newResponse->setVar('updateTime', $updateTime);
615
        $newResponse->setVar('userIP', $userIP);
616
        $newResponse->setVar('private', $private);
617
        if ($responseHandler->insert($newResponse)) {
618
            if ($ret_obj) {
619
                return $newResponse;
620
            }
621
622
            return $newResponse->getVar('id');
623
        }
624
625
        return false;
626
    }
627
628
    /**
629
     * @param bool $includeEmptyValues
630
     * @return array
631
     */
632
    public function &getCustFieldValues(bool $includeEmptyValues = false): array
633
    {
634
        $ticketid = $this->getVar('id');
635
636
        /** @var \XoopsModules\Xhelp\TicketFieldHandler $ticketFieldHandler */
637
        $ticketFieldHandler = $this->helper->getHandler('TicketField');
638
        $fields             = $ticketFieldHandler->getObjects(null);                  // Retrieve custom fields
639
640
        $ticketValuesHandler = $this->helper->getHandler('TicketValues');
641
        $values              = $ticketValuesHandler->get($ticketid);               // Retrieve custom field values
642
        $aCustFields         = [];
643
        foreach ($fields as $field) {
644
            $fileid   = '';
645
            $filename = '';
646
            $value    = '';
647
            $key      = '';
648
            $hasValue = false;
649
            $_arr     = $field->toArray();
650
651
            if (false !== $values
652
                && '' != $values->getVar($field->getVar('fieldname'))) {             // If values for this field has something
653
                $fieldvalues = $field->getVar('fieldvalues');                        // Set fieldvalues
654
                $value       = $key = $values->getVar($field->getVar('fieldname'));  // Value of current field
655
656
                if (\XHELP_CONTROL_YESNO == $field->getVar('controltype')) {
657
                    $value = ((1 == $value) ? _YES : _NO);
658
                }
659
660
                if (\XHELP_CONTROL_FILE == $field->getVar('controltype')) {
661
                    $file     = \explode('_', $value);
662
                    $fileid   = $file[0];
663
                    $filename = $file[1];
664
                }
665
666
                if (\is_array($fieldvalues)) {
667
                    foreach ($fieldvalues as $fkey => $fvalue) {
668
                        if ($fkey == $value) {
669
                            $value = $fvalue;
670
                            break;
671
                        }
672
                    }
673
                }
674
675
                $hasValue = true;
676
            }
677
            $_arr['value']    = $value;
678
            $_arr['fileid']   = $fileid;
679
            $_arr['filename'] = $filename;
680
            $_arr['key']      = $key;
681
682
            if ($includeEmptyValues || $hasValue) {
683
                $aCustFields[$field->getVar('fieldname')] = $_arr;
684
            }
685
        }
686
687
        return $aCustFields;
688
    }
689
}   // end of class
690