PageRenderTime 66ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/incident.inc.php

https://github.com/sitracker/sitracker
PHP | 1956 lines | 1288 code | 222 blank | 446 comment | 218 complexity | 2e4296d3fb029f824d490c70795382db MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, LGPL-2.0
  1. <?php
  2. // incident.inc.php - functions relating to incidents
  3. //
  4. // SiT (Support Incident Tracker) - Support call tracking system
  5. // Copyright (C) 2010-2014 The Support Incident Tracker Project
  6. // Copyright (C) 2000-2009 Salford Software Ltd. and Contributors
  7. //
  8. // This software may be used and distributed according to the terms
  9. // of the GNU General Public License, incorporated herein by reference.
  10. // Prevent script from being run directly (ie. it must always be included
  11. if (realpath(__FILE__) == realpath($_SERVER['SCRIPT_FILENAME']))
  12. {
  13. exit;
  14. }
  15. require_once (APPLICATION_LIBPATH . 'base.inc.php');
  16. require_once (APPLICATION_LIBPATH . 'contract.inc.php');
  17. /**
  18. * Gets incident details
  19. *
  20. * This function emulates a SQL query to the incident table while abstracting
  21. * SQL details
  22. * @param int $incident ID of the incident
  23. * @return object an object containing all parameters contained in the table
  24. * @author Kieran Hogg
  25. */
  26. function incident($incident)
  27. {
  28. global $dbIncidents, $db;
  29. $incident = intval($incident);
  30. $sql = "SELECT * FROM `{$dbIncidents}` WHERE id = '{$incident}'";
  31. $result = mysqli_query($db, $sql);
  32. if (mysqli_error($db)) trigger_error("MySQL Query Error ".mysqli_error($db), E_USER_ERROR);
  33. $row = mysqli_fetch_object($result);
  34. return $row;
  35. }
  36. /**
  37. * Creates a new incident
  38. * @param string $title The title of the incident
  39. * @param int $contact The ID of the incident contact
  40. * @param int $servicelevel The ID of the servicelevel to log the incident under
  41. * @param int $contract The ID of the contract to log the incident under
  42. * @param int $product The ID of the product the incident refers to
  43. * @param int $skill The ID of the skill the incident refers to
  44. * @param string $updatetext The update to open the incident with
  45. * @param int $priority (Optional) Priority of the incident (Default: 1 = Low)
  46. * @param int $owner (Optional) Owner of the incident (Default: 0 = SiT)
  47. * @param int $status (Optional) Incident status (Default: 1 = Active)
  48. * @param string $productversion (Optional) Product version field
  49. * @param string $productservicepacks (Optional) Product service packs field
  50. * @param int $opened (Optional) Timestamp when incident was opened (Default: now)
  51. * @param int $lastupdated (Optional) Timestamp when incident was updated (Default: now)
  52. * @param int $customerid (Optional) The customer reference for the incident
  53. * @return int|bool Returns FALSE on failure, an incident ID on success
  54. * @author Kieran Hogg
  55. */
  56. function create_incident($title, $contact, $servicelevel, $contract, $product,
  57. $software, $updatetext = '', $customervisibility = 'hide', $priority = PRIORITY_LOW, $owner = 0, $status = STATUS_ACTIVE,
  58. $productversion = '', $productservicepacks = '',
  59. $opened = '', $lastupdated = '', $customerid = '')
  60. {
  61. global $now, $dbIncidents, $dbUpdates, $sit, $db;
  62. if (empty($opened))
  63. {
  64. $opened = $now;
  65. }
  66. if (empty($lastupdated))
  67. {
  68. $lastupdated = $now;
  69. }
  70. if (!empty($customerid))
  71. {
  72. $customerid = "'{$customerid}'";
  73. }
  74. else
  75. {
  76. $customerid = "NULL";
  77. }
  78. $sql = "INSERT INTO `{$dbIncidents}` (title, owner, contact, priority, ";
  79. $sql .= "servicelevel, status, maintenanceid, product, softwareid, ";
  80. $sql .= "productversion, productservicepacks, opened, lastupdated, customerid) ";
  81. $sql .= "VALUES ('{$title}', '{$owner}', '{$contact}', '{$priority}', ";
  82. $sql .= "'{$servicelevel}', '{$status}', '{$contract}', ";
  83. $sql .= "'{$product}', '{$software}', '{$productversion}', ";
  84. $sql .= "'{$productservicepacks}', '{$opened}', '{$lastupdated}', $customerid)";
  85. $result = mysqli_query($db, $sql);
  86. if (mysqli_error($db))
  87. {
  88. trigger_error("MySQL Query Error ".mysqli_error($db), E_USER_ERROR);
  89. return FALSE;
  90. }
  91. else
  92. {
  93. $incidentid = mysqli_insert_id($db);
  94. increment_incidents_used($contract);
  95. }
  96. //add the updates and SLA etc
  97. $sql = "INSERT INTO `{$dbUpdates}` (incidentid, userid, type, bodytext, timestamp, currentowner, ";
  98. $sql .= "currentstatus, customervisibility, nextaction, sla) ";
  99. $sql .= "VALUES ('{$incidentid}', '{$sit[2]}', '".UPDATE_TYPE_OPENING."', '{$updatetext}', '{$now}', '{$sit[2]}', ";
  100. $sql .= "'1', '{$customervisibility}', '{$nextaction}', '".UPDATE_SLA_OPENED."')";
  101. $result = mysqli_query($db, $sql);
  102. if (mysqli_error($db)) trigger_error("MySQL Query Error ".mysqli_error($db), E_USER_ERROR);
  103. // Insert the first Review update, this indicates the review period of an incident has started
  104. // This insert could possibly be merged with another of the 'updates' records, but for now we keep it seperate for clarity
  105. $sql = "INSERT INTO `{$dbUpdates}` (incidentid, userid, type, timestamp, currentowner, currentstatus, customervisibility) ";
  106. $sql .= "VALUES ('{$incidentid}', '{$sit[2]}', 'reviewmet', '{$now}', '{$sit[2]}', '1', 'hide')";
  107. mysqli_query($db, $sql);
  108. if (mysqli_error($db)) trigger_error("MySQL Query Error ".mysqli_error($db), E_USER_ERROR);
  109. return $incidentid;
  110. }
  111. /**
  112. * Creates an incident based on an 'tempincoming' table entry
  113. * @author Kieran Hogg
  114. * @param int $incomingid the ID of the tempincoming entry
  115. * @return int|bool returns either the ID of the contract or FALSE if none
  116. */
  117. function create_incident_from_incoming($incomingid)
  118. {
  119. global $dbTempIncoming, $dbMaintenance, $dbServiceLevels, $dbSoftwareProducts, $CONFIG, $db;
  120. $rtn = TRUE;
  121. $incomingid = intval($incomingid);
  122. $sql = "SELECT * FROM `{$dbTempIncoming}` ";
  123. $sql .= "WHERE id = '{$incomingid}'";
  124. $result = mysqli_query($db, $sql);
  125. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_ERROR);
  126. $row = mysqli_fetch_object($result);
  127. $contact = $row->contactid;
  128. $contract = guess_contract_id($contact);
  129. if (!$contract)
  130. {
  131. // we have no contract to log against, update stays in incoming
  132. return TRUE;
  133. }
  134. $subject = $row->subject;
  135. $update = $row->updateid;
  136. $sql = "SELECT tag, product, softwareid ";
  137. $sql .= "FROM `{$dbMaintenance}` AS m, `{$dbServiceLevels}` AS s, ";
  138. $sql .= "`{$dbSoftwareProducts}` AS sp ";
  139. $sql .= "WHERE m.id = '{$contract}' ";
  140. $sql .= "AND m.servicelevel = s.tag ";
  141. $sql .= "AND m.product = sp.productid LIMIT 1";
  142. $result = mysqli_query($db, $sql);
  143. if (mysqli_error($db))
  144. {
  145. trigger_error(mysqli_error($db), E_USER_ERROR);
  146. $rtn = FALSE;
  147. }
  148. $row = mysqli_fetch_object($result);
  149. $sla = $row->tag;
  150. $product = $row->product;
  151. $software = $row->softwareid;
  152. $incident = create_incident($subject, $contact, $row->tag, $contract,
  153. $product, $software);
  154. if (!move_update_to_incident($update, $incident))
  155. {
  156. $rtn = FALSE;
  157. }
  158. else
  159. {
  160. $sql = "DELETE FROM `$dbTempIncoming` WHERE id = '{$incomingid}'";
  161. $result = mysqli_query($db, $sql);
  162. }
  163. if ($CONFIG['auto_assign_incidents'])
  164. {
  165. $user = suggest_reassign_userid($incident);
  166. if (!reassign_incident($incident, $user))
  167. {
  168. $rtn = FALSE;
  169. }
  170. }
  171. return $rtn;
  172. }
  173. /**
  174. * Move an update to an incident
  175. * @author Kieran Hogg
  176. * @param int $update the ID of the update
  177. * @param int $incident the ID of the incident
  178. * @return bool returns TRUE on success, FALSE on failure
  179. */
  180. function move_update_to_incident($update, $incident)
  181. {
  182. global $dbUpdates, $CONFIG, $fsdelim, $db;
  183. $update = intval($update);
  184. $incident = intval($incident);
  185. $sql = "UPDATE `{$dbUpdates}` SET incidentid = '{$incident}' ";
  186. $sql .= "WHERE id = '{$update}'";
  187. mysqli_query($db, $sql);
  188. if (mysqli_error($db))
  189. {
  190. trigger_error(mysqli_error($db), E_USER_ERROR);
  191. return FALSE;
  192. }
  193. else
  194. {
  195. $old_path = $CONFIG['attachment_fspath']. 'updates' . $fsdelim;
  196. $new_path = $CONFIG['attachment_fspath'] . $incident . $fsdelim;
  197. //move attachments from updates to incident
  198. $sql = "SELECT linkcolref, filename FROM `{$GLOBALS['dbLinks']}` AS l, ";
  199. $sql .= "`{$GLOBALS['dbFiles']}` as f ";
  200. $sql .= "WHERE l.origcolref = '{$update}' ";
  201. $sql .= "AND l.linktype = 5 ";
  202. $sql .= "AND l.linkcolref = f.id";
  203. $result = mysqli_query($db, $sql);
  204. if ($result)
  205. {
  206. if (!file_exists($new_path))
  207. {
  208. $umask = umask(0000);
  209. @mkdir($new_path, 0770);
  210. umask($umask);
  211. }
  212. while ($row = mysqli_fetch_object($result))
  213. {
  214. $filename = $row->linkcolref . "-" . $row->filename;
  215. $old_file = $old_path . $row->linkcolref;
  216. if (file_exists($old_file))
  217. {
  218. $rename = rename($old_file, $new_path . $filename);
  219. if (!$rename)
  220. {
  221. trigger_error("Couldn't move file: {$file}", E_USER_WARNING);
  222. $moved_attachments = FALSE;
  223. }
  224. }
  225. }
  226. }
  227. return TRUE;
  228. }
  229. }
  230. /**
  231. * Gets update details
  232. *
  233. * This function emulates a SQL query to the update table while abstracting
  234. * SQL details
  235. * @param int $update ID of the update
  236. * @return object an object containing all parameters contained in the table
  237. * @author Kieran Hogg
  238. */
  239. function update($update)
  240. {
  241. global $dbUpdates, $db;
  242. $update = intval($update);
  243. $sql = "SELECT * FROM `{$dbUpdates}` WHERE id = '{$update}'";
  244. $result = mysqli_query($db, $sql);
  245. if (mysqli_error($db)) trigger_error("MySQL Query Error ".mysqli_error($db), E_USER_ERROR);
  246. $row = mysqli_fetch_object($result);
  247. return $row;
  248. }
  249. /**
  250. * Suggest the userid of a suitable person to handle the given incident
  251. * @author Ivan Lucas
  252. * @param int $incidentid. An incident ID to suggest a new owner for
  253. * @param int $exceptuserid. This user ID will not be suggested (e.g. the existing owner)
  254. * @return A user ID of the suggested new owner
  255. * @retval bool FALSE failure.
  256. * @retval int The user ID of the suggested new owner
  257. * @note Users are chosen randomly in a weighted lottery depending on their
  258. * avilability and queue status
  259. */
  260. function suggest_reassign_userid($incidentid, $exceptuserid = 0)
  261. {
  262. global $now, $dbUsers, $dbIncidents, $dbUserSoftware, $startofsession, $db;
  263. $ticket = array();
  264. $sql = "SELECT product, softwareid, priority, contact, owner FROM `{$dbIncidents}` WHERE id={$incidentid} LIMIT 1";
  265. $result = mysqli_query($db, $sql);
  266. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  267. if (!$result)
  268. {
  269. $userid = FALSE;
  270. }
  271. else
  272. {
  273. $incident = mysqli_fetch_object($result);
  274. // If this is a critical incident the user we're assigning to must be online
  275. if ($incident->priority >= PRIORITY_CRITICAL)
  276. {
  277. $req_online = TRUE;
  278. }
  279. else
  280. {
  281. $req_online = FALSE;
  282. }
  283. // Find the users with this skill (or all users)
  284. if (!empty($incident->softwareid))
  285. {
  286. $sql = "SELECT us.userid, u.status, u.lastseen FROM `{$dbUserSoftware}` AS us, `{$dbUsers}` AS u ";
  287. $sql .= "WHERE u.id = us.userid AND u.status > 0 AND u.accepting='Yes' ";
  288. if ($exceptuserid > 0) $sql .= "AND u.id != '{$exceptuserid}' ";
  289. $sql .= "AND softwareid = {$incident->softwareid}";
  290. }
  291. else
  292. {
  293. $sql = "SELECT id AS userid, status, lastseen FROM `{$dbUsers}` AS u WHERE status > 0 AND u.accepting='Yes' ";
  294. if ($exceptuserid > 0) $sql .= "AND id != '{$exceptuserid}' ";
  295. }
  296. $result = mysqli_query($db, $sql);
  297. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  298. // Fallback to all users if we have no results from above
  299. if (mysqli_num_rows($result) < 1)
  300. {
  301. $sql = "SELECT id AS userid, status, lastseen FROM `{$dbUsers}` AS u WHERE status > 0 AND u.accepting='Yes' ";
  302. if ($exceptuserid > 0) $sql .= "AND id != '{$exceptuserid}' ";
  303. $result = mysqli_query($db, $sql);
  304. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  305. }
  306. while ($user = mysqli_fetch_object($result))
  307. {
  308. // Get a ticket for being skilled
  309. // Or in the case we don't know the skill, just get a ticket for accepting
  310. $ticket[] = $user->userid;
  311. // Get a ticket for being seen within the current session time
  312. if (mysql2date($user->lastseen) > $startofsession) $ticket[] = $user->userid;
  313. // Get two tickets for being marked in-office or working at home
  314. if ($user->status == USERSTATUS_IN_OFFICE OR $user->status == USERSTATUS_WORKING_FROM_HOME)
  315. {
  316. $ticket[] = $user->userid;
  317. $ticket[] = $user->userid;
  318. }
  319. // Get one ticket for being marked at lunch or in meeting
  320. // BUT ONLY if the incident isn't critical
  321. if ($incident->priority < PRIORITY_CRITICAL AND ($user->status == USERSTATUS_IN_MEETING OR $user->status == USERSTATUS_AT_LUNCH))
  322. {
  323. $ticket[] = $user->userid;
  324. }
  325. // Have a look at the users (all open) incident queue (owned)
  326. $qsql = "SELECT id, priority, lastupdated, status, softwareid FROM `{$dbIncidents}` WHERE owner={$user->userid} AND status != " . STATUS_CLOSED . " AND status != " . STATUS_CLOSING;
  327. $qresult = mysqli_query($db, $qsql);
  328. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  329. $queue_size = mysqli_num_rows($qresult);
  330. if ($queue_size > 0)
  331. {
  332. $queued_critical = 0;
  333. $queued_high = 0;
  334. $queue_lastupdated = 0;
  335. $queue_samecontact = FALSE;
  336. while ($queue = mysqli_fetch_object($qresult))
  337. {
  338. if ($queue->priority == PRIORITY_HIGH) $queued_high++;
  339. if ($queue->priority >= PRIORITY_CRITICAL) $queued_critical++;
  340. if ($queue->lastupdated > $queue_lastupdated) $queue_lastupdated = $queue->lastupdated;
  341. if ($queue->contact == $incident->contact) $queue_samecontact = TRUE;
  342. }
  343. // Get one ticket for your queue being updated in the past 4 hours
  344. if ($queue_lastupdated > ($now - 14400)) $ticket[] = $user->userid;
  345. // Get two tickets for dealing with the same contact in your queue
  346. if ($queue_samecontact == TRUE)
  347. {
  348. $ticket[] = $user->userid;
  349. $ticket[] = $user->userid;
  350. }
  351. // Get one ticket for having five or less incidents
  352. if ($queue_size <= 5) $ticket[] = $user->userid;
  353. // Get up to three tickets, one less ticket for each critical incident in queue
  354. for ($c = 1; $c < (3 - $queued_critical); $c++)
  355. {
  356. $ticket[] = $user->userid;
  357. }
  358. // Get up to three tickets, one less ticket for each high priority incident in queue
  359. for ($c = 1; $c < (3 - $queued_high); $c++)
  360. {
  361. $ticket[] = $user->userid;
  362. }
  363. }
  364. else
  365. {
  366. // Get one ticket for having an empty queue
  367. $ticket[] = $user->userid;
  368. }
  369. }
  370. // Do the lottery - "Release the balls"
  371. $numtickets = count($ticket) - 1;
  372. // Ensure we return a failure if we have a negative amount of tickets
  373. if ($numtickets < 0)
  374. {
  375. return FALSE;
  376. }
  377. $rand = mt_rand(0, $numtickets);
  378. $userid = $ticket[$rand];
  379. }
  380. if (empty($userid)) $userid = FALSE;
  381. return $userid;
  382. }
  383. /**
  384. * Reassigns an incident
  385. * @param int $incident incident ID to reassign
  386. * @param int $user user to reassign the incident to
  387. * @param string $type 'full' to do a full reassign, 'temp' for a temp
  388. * @return bool TRUE on success, FALSE on failure
  389. * @author Kieran Hogg
  390. */
  391. function reassign_incident($incident, $user, $tuser = '', $nextaction = '', $type = 'full')
  392. {
  393. global $dbIncidents, $dbUpdates, $now, $sit, $db;
  394. $rtn = TRUE;
  395. if ($type == 'temp')
  396. {
  397. $sql = "UPDATE `{$dbIncidents}` SET towner = '{$tuser}' ";
  398. }
  399. else
  400. {
  401. $sql = "UPDATE `{$dbIncidents}` SET owner = '{$user}' ";
  402. }
  403. $sql .= "WHERE id = '{$incident}'";
  404. mysqli_query($db, $sql);
  405. if (mysqli_error($db))
  406. {
  407. trigger_error(mysqli_error($db), E_USER_WARNING);
  408. $rtn = FALSE;
  409. }
  410. $sql = "INSERT INTO `{$dbUpdates}` (incidentid, userid, type, timestamp, currentowner, currentstatus, nextaction) ";
  411. $sql .= "VALUES ('{$incident}', '{$sit[2]}', 'reassigning', '{$now}', '{$user}', '1', '{$nextaction}')";
  412. $result = mysqli_query($db, $sql);
  413. if (mysqli_error($db))
  414. {
  415. trigger_error(mysqli_error($db), E_USER_WARNING);
  416. $rtn = FALSE;
  417. }
  418. return $rtn;
  419. }
  420. /**
  421. * Reopens an incident
  422. * @param int $incident incident ID to reopen
  423. * @param int $newstatus (optional) status to set the incident to, defaults to active
  424. * @param string $message (optional) message to insert when reopening
  425. * @return bool TRUE on success, FALSE on failure$dbIncidents
  426. * @author Kieran Hogg
  427. */
  428. function reopen_incident($incident, $newstatus = STATUS_ACTIVE, $message = '')
  429. {
  430. global $dbIncidents, $dbUpdates, $now, $sit, $bodytext, $db;
  431. $rtn = TRUE;
  432. $time = time();
  433. $sql = "UPDATE `{$dbIncidents}` SET status='{$newstatus}', ";
  434. $sql .= "lastupdated='{$time}', closed='0', owner='0', towner='0' WHERE id='{$incident}' LIMIT 1";
  435. mysqli_query($db, $sql);
  436. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_ERROR);
  437. $owner = incident_owner($incident);
  438. // add update
  439. $sql = "INSERT INTO `{$dbUpdates}` (incidentid, userid, type, ";
  440. $sql .= "bodytext, timestamp, currentowner, currentstatus) ";
  441. $sql .= "VALUES ({$incident}, '{$sit[2]}', 'reopening', '{$bodytext}', '{$time}', ";
  442. $sql .= "'{$owner}', '{$newstatus}')";
  443. $result = mysqli_query($db, $sql);
  444. if (mysqli_error($db))
  445. {
  446. trigger_error(mysqli_error($db), E_USER_ERROR);
  447. $rtn = FALSE;
  448. }
  449. // Insert the first SLA update for the reopened incident, this indicates
  450. // the start of an sla period
  451. // This insert could possibly be merged with another of the 'updates'
  452. // records, but for now we keep it seperate for clarity
  453. $sql = "INSERT INTO `{$dbUpdates}` (incidentid, userid, type, ";
  454. $sql .= "timestamp, currentowner, currentstatus, customervisibility, ";
  455. $sql .= "sla, bodytext) ";
  456. $sql .= "VALUES ('{$incident}', '{$sit[2]}', 'slamet', '{$now}', '{$owner}', ";
  457. $sql .= STATUS_ACTIVE.", 'show', 'opened', '{$GLOBALS['strIncidentIsOpen']}')";
  458. mysqli_query($db, $sql);
  459. if (mysqli_error($db))
  460. {
  461. trigger_error(mysqli_error($db), E_USER_ERROR);
  462. $rtn = FALSE;
  463. }
  464. // Insert the first Review update, this indicates the review period of an incident has restarted
  465. // This insert could possibly be merged with another of the 'updates' records, but for now we keep it seperate for clarity
  466. $sql = "INSERT INTO `{$dbUpdates}` (incidentid, userid, type, timestamp, currentowner, currentstatus, customervisibility, sla, bodytext) ";
  467. $sql .= "VALUES ('{$incident}', '0', 'reviewmet', '{$now}', '{$owner}', ".STATUS_ACTIVE.", 'hide', 'opened','')";
  468. mysqli_query($db, $sql);
  469. if (mysqli_error($db))
  470. {
  471. trigger_error(mysqli_error($db), E_USER_ERROR);
  472. $rtn = FALSE;
  473. }
  474. return $rtn;
  475. }
  476. /**
  477. * Send a template email without using a trigger
  478. * @author Ivan Lucas
  479. * @param int $templateid: The ID number of the template to use
  480. * @param array $paramarray. An associative array of template parameters
  481. This should at the very least be
  482. array('incidentid' => $id, 'triggeruserid' => $sit[2])
  483. * @param string $attach. Path and filename of file to attach
  484. * @param string $attachtype. Type of file to attach (Default 'OCTET')
  485. * @param string $attachdesc. Description of the attachment, (Default, same as filename)
  486. * @return bool TRUE: The email was sent successfully, FALSE: There was an error sending the mail
  487. * @note This is v2 of this function, it has different paramters than v1
  488. */
  489. function send_email_template($templateid, $paramarray, $attach='', $attachtype='', $attachdesc='')
  490. {
  491. global $CONFIG, $application_version_string, $sit;
  492. if (!is_array($paramarray))
  493. {
  494. trigger_error("Invalid Parameter Array", E_USER_NOTICE);
  495. $paramarray = array('triggeruserid' => $sit[2]);
  496. }
  497. if (!is_numeric($templateid))
  498. {
  499. trigger_error("Invalid Template ID '{$templateid}'", E_USER_NOTICE);
  500. }
  501. // Grab the template
  502. $tsql = "SELECT * FROM `{$dbEmailTemplates}` WHERE id={$templateid} LIMIT 1";
  503. $tresult = mysqli_query($db, $tsql);
  504. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  505. if (mysqli_num_rows($tresult) > 0) $template = mysqli_fetch_object($tresult);
  506. $paramarray = array('incidentid' => $paramarray['incidentid'], 'triggeruserid' => $sit[2]);
  507. if ($CONFIG['outbound_email_newline'] == 'CRLF')
  508. {
  509. $crlf = "\r\n";
  510. }
  511. else
  512. {
  513. $crlf = "\n";
  514. }
  515. $from = replace_specials($template->fromfield, $paramarray);
  516. $replyto = replace_specials($template->replytofield, $paramarray);
  517. $ccemail = replace_specials($template->ccfield, $paramarray);
  518. $bccemail = replace_specials($template->bccfield, $paramarray);
  519. $toemail = replace_specials($template->tofield, $paramarray);
  520. $subject = replace_specials($template->subjectfield, $paramarray);
  521. $body = replace_specials($template->body, $paramarray);
  522. $extra_headers = "Reply-To: {$replyto}{$crlf}Errors-To: ".user_email($sit[2]) . $crlf;
  523. if ($CONFIG['outbound_email_send_xoriginatingip']) $extra_headers .= "X-Mailer: {$CONFIG['application_shortname']} {$application_version_string}/PHP " . phpversion() . "\n";
  524. $extra_headers .= "X-Originating-IP: " . substr($_SERVER['REMOTE_ADDR'],0, 15) . "\n";
  525. if ($ccemail != '') $extra_headers .= "CC: $ccemail\n";
  526. if ($bccemail != '') $extra_headers .= "BCC: $bccemail\n";
  527. $extra_headers .= "\n"; // add an extra crlf to create a null line to separate headers from body
  528. // this appears to be required by some email clients - INL
  529. // Removed $mailerror as MIME_mail expects 5 args and not 6 of which is it not expect errors
  530. $mime = new MIME_mail($from, $toemail, html_entity_decode($subject), '', $extra_headers);
  531. $mime -> attach($body, '', "text-plain; charset={$GLOBALS['i18ncharset']}", $CONFIG['outbound_email_encoding']);
  532. if (!empty($attach))
  533. {
  534. if (empty($attachdesc)) $attachdesc = "Attachment named {$attach}";
  535. $disp = "attachment; filename=\"{$attach}\"; name=\"{$attach}\";";
  536. $mime -> fattach($attach, $attachdesc, $attachtype, 'base64', $disp);
  537. }
  538. // actually send the email
  539. $rtnvalue = $mime -> send_mail();
  540. return $rtnvalue;
  541. }
  542. /**
  543. * Identified if there are drafts waiting to be sent/updated on an incident
  544. * @author Paul Heaney
  545. * @param int $incidentid - The incidentID to check for
  546. * @param string $type - The type of draft either all/email/update
  547. * @return bool TRUE of there are drafts waiting false otherwise
  548. */
  549. function drafts_waiting_on_incident($incidentid, $type='all', $userid='')
  550. {
  551. global $db;
  552. $rtn = FALSE;
  553. $sql = "SELECT count(id) AS count FROM `{$GLOBALS['dbDrafts']}` WHERE incidentid = {$incidentid} ";
  554. if ($type == "update") $sql .= "AND type = 'update' ";
  555. elseif ($type == "email") $sql .= "AND type = 'email' ";
  556. if (!empty($userid)) $sql .= "AND userid = {$userid} ";
  557. $result = mysqli_query($db, $sql);
  558. if (mysqli_error($db))
  559. {
  560. trigger_error(mysqli_error($db), E_USER_ERROR);
  561. $rtn = FALSE;
  562. }
  563. list($count) = mysqli_fetch_row($result);
  564. if ($count > 0) $rtn = TRUE;
  565. return $rtn;
  566. }
  567. /**
  568. * Gets the incident ID for an email based on its subject
  569. * @author Kieran Hogg
  570. * @param string $subject The email subject
  571. * @param string $from The email address it was sent from
  572. * @return int ID of the incident, 0 if none
  573. */
  574. function incident_id_from_subject($subject, $from)
  575. {
  576. global $CONFIG, $db;
  577. $incident_id = FALSE;
  578. $from_parts = explode($from, "@");
  579. $domain = $from_parts[2];
  580. $open = escape_regex($CONFIG['incident_id_email_opening_tag']);
  581. $close = escape_regex($CONFIG['incident_id_email_closing_tag']);
  582. $prefix = escape_regex($CONFIG['incident_reference_prefix']);
  583. if (preg_match("/{$open}{$prefix}(\d+){$close}/", $subject, $m))
  584. {
  585. $incident_id = $m[1];
  586. }
  587. else
  588. {
  589. preg_match('/\d{1,12}/', $subjectm, $external_id);
  590. $external_id = $external_id[0];
  591. $sql = "SELECT name, email_domain FROM `{$dbEscalationPaths}`";
  592. $result = mysqli_query($db, $sql);
  593. if ($result)
  594. {
  595. while ($obj = mysqli_fetch_object($result))
  596. {
  597. if ($obj->email_domain == $domain)
  598. {
  599. $sql_ext = "SELECT id FROM `{$dbIncidents}` ";
  600. $sql_ext .= "WHERE externalid = '{$external_id}'";
  601. $result_ext = mysqli_query($db, $sql_ext);
  602. if (mysqli_num_rows($result_ext) != 1)
  603. {
  604. $o = mysqli_fetch_object($result_ext);
  605. $incident_id = $o->id;
  606. }
  607. }
  608. }
  609. }
  610. }
  611. return $incident_id;
  612. }
  613. /**
  614. * @author Ivan Lucas
  615. */
  616. function count_incident_stats($incidentid)
  617. {
  618. global $dbUpdates, $db;
  619. $sql = "SELECT count(DISTINCT currentowner),count(id) FROM `{$dbUpdates}` WHERE incidentid='{$incidentid}' AND userid!=0 GROUP BY userid";
  620. $result = mysqli_query($db, $sql);
  621. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  622. list($unique_users,$num_updates) = mysqli_fetch_row($result);
  623. return array($unique_users,$num_updates);;
  624. }
  625. /**
  626. * Returns number of closed incidents that were opened within the period giving
  627. * the average duration in minutes and the average worked time in minutes
  628. * @author Ivan Lucas
  629. */
  630. function average_incident_duration($start,$end,$states)
  631. {
  632. global $dbIncidents, $db;
  633. $sql = "SELECT opened, closed, (closed - opened) AS duration_closed, i.id AS incidentid ";
  634. $sql .= "FROM `{$dbIncidents}` AS i ";
  635. $sql .= "WHERE status = '" . STATUS_CLOSED . "' ";
  636. if ($start > 0) $sql .= "AND opened >= {$start} ";
  637. if ($end > 0) $sql .= "AND opened <= {$end} ";
  638. $result = mysqli_query($db, $sql);
  639. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  640. $totalduration = 0;
  641. $totalworkingduration = 0;
  642. $countclosed = 0;
  643. $total_unique_owners= 0;
  644. while ($row = mysqli_fetch_object($result))
  645. {
  646. $working_time = calculate_incident_working_time($row->incidentid, $row->opened, $row->closed, $states);
  647. if ($row->duration_closed > 0)
  648. {
  649. $totalduration = $totalduration+$row->duration_closed;
  650. $totalworkingduration += $working_time;
  651. $cio = count_incident_stats($row->incidentid);
  652. $total_unique_owners += $cio[0];
  653. $total_updates += $cio[1];
  654. $countclosed++;
  655. }
  656. }
  657. $total_number_updates = number_format(($countclosed == 0) ? 0 : ($total_updates / $countclosed),1);
  658. $average_owners = number_format(($countclosed == 0) ? 0 : ($total_unique_owners / $countclosed),1);
  659. $average_incident_duration = ($countclosed == 0) ? 0 : ($totalduration / $countclosed) / 60;
  660. $average_worked_minutes = ($countclosed == 0) ? 0 : $totalworkingduration / $countclosed;
  661. return array($countclosed, $average_incident_duration, $average_worked_minutes,$average_owners, $total_updates, $total_number_updates);
  662. }
  663. /**
  664. * Returns the contents of an SLA target update, mostly for problem definition and action plan to pre-fill the close form
  665. * @author Kieran Hogg
  666. * @param $incidentid int The incident to get the update of
  667. * @param $target string The SLA target, initialresponse, probdef etc
  668. * @return string The updates of the message, stripped of line breaks
  669. */
  670. function sla_target_content($incidentid, $target)
  671. {
  672. global $db, $dbUpdates;
  673. $rtn = '';
  674. $incidentid = clean_int($incidentid);
  675. $target = clean_dbstring($target);
  676. $sql = "SELECT bodytext FROM `{$dbUpdates}` ";
  677. $sql .= "WHERE incidentid = '{$incidentid}' ";
  678. $sql .= "AND sla = '{$target}' ";
  679. $sql .= "ORDER BY timestamp DESC";
  680. $result = mysqli_query($db, $sql);
  681. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  682. list($bodytext) = mysqli_fetch_assoc($result);
  683. $bodytext = str_replace("<hr>", "", $bodytext);
  684. $rtn .= $bodytext;
  685. return $rtn;
  686. }
  687. /**
  688. * Retreive the service level tag of the incident
  689. * @author Paul Heaney
  690. * @param $incidentid int The incident ID to retreive
  691. * @return String The service level tag
  692. * @todo Remove as of 4.0 in favour of incidents class
  693. */
  694. function incident_service_level($incidentid)
  695. {
  696. global $dbIncidents, $db;
  697. $servicelevel = '';
  698. $sql = "SELECT servicelevel FROM `{$dbIncidents}` WHERE id = {$incidentid}";
  699. $result = mysqli_query($db, $sql);
  700. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  701. if (mysqli_num_rows($result) > 0)
  702. {
  703. $servicelevel = mysqli_fetch_object($result)->servicelevel;
  704. }
  705. return $servicelevel;
  706. }
  707. /**
  708. * Load the incident entitlement for the portal
  709. * Moved from portal/ad.php
  710. * @author Kieran Hogg
  711. * @param $contactid - The contact to load the entitlement for
  712. * @param $siteid - The site the contact belongs to
  713. */
  714. function load_entitlements($contactid, $siteid)
  715. {
  716. global $dbSupportContacts, $dbMaintenance, $dbProducts, $db;
  717. //get entitlement
  718. $sql = "SELECT m.*, p.name, ";
  719. $sql .= "(m.incident_quantity - m.incidents_used) AS availableincidents ";
  720. $sql .= "FROM `{$dbSupportContacts}` AS sc, `{$dbMaintenance}` AS m, `{$dbProducts}` AS p ";
  721. $sql .= "WHERE m.product=p.id ";
  722. $sql .= "AND sc.contactid='{$contactid}' AND sc.maintenanceid=m.id ";
  723. $sql .= "AND (expirydate > (UNIX_TIMESTAMP(NOW()) - 15778463) OR expirydate = -1) ";
  724. $sql .= "AND m.site = {$siteid} ";
  725. $sql .= "UNION SELECT m.*, p.name, ";
  726. $sql .= "(m.incident_quantity - m.incidents_used) AS availableincidents ";
  727. $sql .= "FROM `{$dbSupportContacts}` AS sc, `{$dbMaintenance}` AS m, `{$dbProducts}` AS p ";
  728. $sql .= "WHERE m.product=p.id ";
  729. $sql .= "AND m.allcontactssupported = 'yes' ";
  730. $sql .= "AND (expirydate > (UNIX_TIMESTAMP(NOW()) - 15778463) OR expirydate = -1) ";
  731. $sql .= "AND m.site = {$siteid} ";
  732. $sql .= "ORDER BY expirydate DESC ";
  733. $contractresult = mysqli_query($db, $sql);
  734. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  735. unset($_SESSION['entitlement']);
  736. while ($contract = mysqli_fetch_object($contractresult))
  737. {
  738. $_SESSION['entitlement'][] = serialize($contract);
  739. }
  740. }
  741. /**
  742. * Get a readable last update body, written for the triggers variable
  743. * @param $incidentid int The incident ID to get the update for
  744. * @param $num int amount of updates to include
  745. */
  746. function readable_last_updates($incidentid, $num)
  747. {
  748. global $dbUpdates, $db;
  749. $num = intval($num);
  750. ($num == 0) ? $num = 1 : $num;
  751. $sql = "SELECT * FROM `{$dbUpdates}` ";
  752. $sql .= " WHERE incidentid='{$incidentid}' ";
  753. $sql .= "AND bodytext != '' ";
  754. $sql .= "ORDER BY timestamp DESC ";
  755. if ($num != -1 ) $sql .= "LIMIT {$num}";
  756. $query = mysqli_query($db, $sql);
  757. $text = "";
  758. while ($result = mysqli_fetch_object($query))
  759. {
  760. $num--;
  761. $text .= strip_tags($result->bodytext);
  762. if ($num > 0 ) $text .= "\n--------------------------\n";
  763. }
  764. return $text;
  765. }
  766. /**
  767. * @author Ivan Lucas
  768. * @param int $id Incident ID
  769. * @return integer. UserID of the user that currently owns the incident
  770. */
  771. function incident_owner($id)
  772. {
  773. return db_read_column('owner', $GLOBALS['dbIncidents'], $id);
  774. }
  775. /**
  776. * @author Ivan Lucas
  777. * @param int $id Incident ID
  778. * @return integer. UserID of the user that currently temporarily owns the incident
  779. */
  780. function incident_towner($id)
  781. {
  782. return db_read_column('towner', $GLOBALS['dbIncidents'], $id);
  783. }
  784. /**
  785. * @author Ivan Lucas
  786. * @param int $id Incident ID
  787. * @return integer. ContactID of the contact this incident is logged against
  788. */
  789. function incident_contact($id)
  790. {
  791. return db_read_column('contact', $GLOBALS['dbIncidents'], $id);
  792. }
  793. /**
  794. * @author Ivan Lucas
  795. * @param int $id Incident ID
  796. * @return integer. Contract ID of the maintenance contract this incident is logged against
  797. */
  798. function incident_maintid($id)
  799. {
  800. $maintid = db_read_column('maintenanceid', $GLOBALS['dbIncidents'], $id);
  801. if ($maintid == '')
  802. {
  803. trigger_error("!Error: No matching record while reading in incident_maintid() Incident ID: {$id}", E_USER_WARNING);
  804. }
  805. else
  806. {
  807. return ($maintid);
  808. }
  809. }
  810. /**
  811. * @author Ivan Lucas
  812. * @param int $id Incident ID
  813. * @return string. Title of the incident
  814. */
  815. function incident_title($id)
  816. {
  817. return db_read_column('title', $GLOBALS['dbIncidents'], $id);
  818. }
  819. /**
  820. * @author Ivan Lucas
  821. * @param int $id Incident ID
  822. * @return id. Current incident status ID
  823. */
  824. function incident_status($id)
  825. {
  826. return db_read_column('status', $GLOBALS['dbIncidents'], $id);
  827. }
  828. /**
  829. * @author Ivan Lucas
  830. * @param int $id Incident ID
  831. * @return id. Current incident Priority ID
  832. */
  833. function incident_priority($id)
  834. {
  835. return db_read_column('priority', $GLOBALS['dbIncidents'], $id);
  836. }
  837. /**
  838. * @author Ivan Lucas
  839. * @param int $id Incident ID
  840. * @return id. Current incident external ID
  841. */
  842. function incident_externalid($id)
  843. {
  844. return db_read_column('externalid', $GLOBALS['dbIncidents'], $id);
  845. }
  846. /**
  847. * @author Paul Heaney
  848. * @param int $id Incident ID
  849. * @return id. Current incident customer ID
  850. */
  851. function incident_customerid($id)
  852. {
  853. return db_read_column('customerid', $GLOBALS['dbIncidents'], $id);
  854. }
  855. /**
  856. * @author Ivan Lucas
  857. * @param int $id Incident ID
  858. * @return string. Current incident external engineer
  859. */
  860. function incident_externalengineer($id)
  861. {
  862. return db_read_column('externalengineer', $GLOBALS['dbIncidents'], $id);
  863. }
  864. /**
  865. * @author Ivan Lucas
  866. * @param int $id Incident ID
  867. * @return string. Current incident external email address
  868. */
  869. function incident_externalemail($id)
  870. {
  871. return db_read_column('externalemail', $GLOBALS['dbIncidents'], $id);
  872. }
  873. /**
  874. * @author Ivan Lucas
  875. * @param int $id Incident ID
  876. * @return string. Current incident CC email address
  877. */
  878. function incident_ccemail($id)
  879. {
  880. return db_read_column('ccemail', $GLOBALS['dbIncidents'], $id);
  881. }
  882. /**
  883. * @author Ivan Lucas
  884. * @param int $id Incident ID
  885. * @return int. UNIX Timestamp of the time of the next action for this incident
  886. */
  887. function incident_timeofnextaction($id)
  888. {
  889. return db_read_column('timeofnextaction', $GLOBALS['dbIncidents'], $id);
  890. }
  891. /**
  892. * Returns a string representing the name of
  893. * the given priority. Returns an empty string if the
  894. * priority does not exist.
  895. * @author Ivan Lucas
  896. * @param int $id. Priority ID, higher the number higher the priority
  897. * @param bool $syslang. (optional) Uses system language when set to TRUE otherwise
  898. * uses user language (default)
  899. * @return string.
  900. */
  901. function priority_name($id, $syslang = FALSE)
  902. {
  903. switch ($id)
  904. {
  905. case PRIORITY_LOW:
  906. if (!$syslang) $value = $GLOBALS['strLow'];
  907. else $value = $_SESSION['syslang']['strLow'];
  908. break;
  909. case PRIORITY_MEDIUM:
  910. if (!$syslang) $value = $GLOBALS['strMedium'];
  911. else $value = $_SESSION['syslang']['strMedium'];
  912. break;
  913. case PRIORITY_HIGH:
  914. if (!$syslang) $value = $GLOBALS['strHigh'];
  915. else $value = $_SESSION['syslang']['strHigh'];
  916. break;
  917. case PRIORITY_CRITICAL:
  918. if (!$syslang) $value = $GLOBALS['strCritical'];
  919. else $value = $_SESSION['syslang']['strCritical'];
  920. break;
  921. case '':
  922. if (!$sylang) $value = $GLOBALS['strNotSet'];
  923. else $value = $_SESSION['syslang']['strNotSet'];
  924. break;
  925. default:
  926. if (!$syslang) $value = $GLOBALS['strUnknown'];
  927. else $value = $_SESSION['syslang']['strUnknown'];
  928. break;
  929. }
  930. return $value;
  931. }
  932. /**
  933. * Returns an array of fields from the most recent update record for a given incident id
  934. * @author Ivan Lucas
  935. * @param int $id An incident ID
  936. * @return array
  937. */
  938. function incident_lastupdate($id)
  939. {
  940. global $db;
  941. // Find the most recent update
  942. $sql = "SELECT userid, type, sla, currentowner, currentstatus, bodytext AS body, timestamp, nextaction, id ";
  943. $sql .= "FROM `{$GLOBALS['dbUpdates']}` WHERE incidentid='{$id}' AND bodytext != '' ORDER BY timestamp DESC, id DESC LIMIT 1";
  944. $result = mysqli_query($db, $sql);
  945. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  946. if (mysqli_num_rows($result) == 0)
  947. {
  948. trigger_error("Zero records while retrieving incident last update for incident {$id}", E_USER_WARNING);
  949. }
  950. else
  951. {
  952. $update = mysqli_fetch_object($result);
  953. mysqli_free_result($result);
  954. $offset = 500;
  955. if (strlen($update->body) > $offset)
  956. {
  957. // Only truncate if more than $offset in length
  958. $pos = strpos($update->body, " ", $offset);
  959. if (!empty($pos))
  960. {
  961. // Only truncate if longer than 500 characters
  962. $update->body = substr($update->body, 0, $pos);
  963. }
  964. }
  965. // Remove Tags from update Body
  966. $update->body = trim($update->body);
  967. $update->body = $update->body;
  968. return array($update->userid, $update->type ,$update->currentowner, $update->currentstatus, $update->body, $update->timestamp, $update->nextaction, $update->id);
  969. }
  970. }
  971. /**
  972. * Returns a string containing the body of the first update (that is visible to customer)
  973. * in a format suitable for including in an email
  974. * @author Ivan Lucas
  975. * @param int $id An incident ID
  976. */
  977. function incident_firstupdate($id)
  978. {
  979. global $db;
  980. $sql = "SELECT bodytext FROM `{$GLOBALS['dbUpdates']}` WHERE incidentid='{$id}' AND customervisibility='show' ";
  981. $sql .= "ORDER BY timestamp ASC, id ASC LIMIT 1";
  982. $result = mysqli_query($db, $sql);
  983. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  984. if (mysqli_num_rows($result) >= 1)
  985. {
  986. list($bodytext) = mysqli_fetch_row($result);
  987. $bodytext = strip_tags($bodytext);
  988. }
  989. else
  990. {
  991. $bodytext = '';
  992. }
  993. return $bodytext;
  994. }
  995. /**
  996. * Converts an incident status ID to an internationalised status string
  997. * @author Ivan Lucas
  998. * @param int $id. incident status ID
  999. * @param string $type. 'internal' or 'external', where external means customer/client facing
  1000. * @return string Internationalised incident status.
  1001. * Or empty string if the ID is not recognised.
  1002. * @note The incident status database table must contain i18n keys.
  1003. */
  1004. function incidentstatus_name($id, $type='internal')
  1005. {
  1006. global $dbIncidentStatus, $db;
  1007. if ($type == 'external')
  1008. {
  1009. $type = 'ext_name';
  1010. }
  1011. else
  1012. {
  1013. $type = 'name';
  1014. }
  1015. $sql = "SELECT {$type} FROM `{$dbIncidentStatus}` WHERE id='{$id}'";
  1016. $result = mysqli_query($db, $sql);
  1017. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  1018. if (mysqli_num_rows($result) == 0)
  1019. {
  1020. $name = '';
  1021. }
  1022. else
  1023. {
  1024. $incidentstatus = mysqli_fetch_assoc($result);
  1025. $name = $GLOBALS[$incidentstatus[$type]];
  1026. }
  1027. return $name;
  1028. }
  1029. /**
  1030. * Convert a closing status ID to a readable name
  1031. * @param int $id. Closing status ID
  1032. * @retval string Closing Status
  1033. */
  1034. function closingstatus_name($id)
  1035. {
  1036. global $dbClosingStatus;
  1037. if ($id != '')
  1038. {
  1039. $closingstatus = db_read_column('name', $GLOBALS['dbClosingStatus'], $id);
  1040. }
  1041. else
  1042. {
  1043. $closingstatus = 'strUnknown';
  1044. }
  1045. return ($GLOBALS[$closingstatus]);
  1046. }
  1047. /**
  1048. * Returns the number of remaining incidents given an incident pool id
  1049. *
  1050. * @param int $id. Pool ID
  1051. * @retval int The number of incidents remaining
  1052. * @retval string Returns a string meaning 'Unlimited' if theres no match on ID
  1053. */
  1054. function incidents_remaining($id)
  1055. {
  1056. $remaining = db_read_column('incidentsremaining', $GLOBALS['dbIncidentPools'], $id);
  1057. if (empty($remaining))
  1058. {
  1059. $remaining = '&infin;';
  1060. }
  1061. return $remaining;
  1062. }
  1063. /**
  1064. * Decrement a 'free' incident from a site by one
  1065. *
  1066. * @param int $siteid. Site ID
  1067. * @retval TRUE success
  1068. */
  1069. function decrement_free_incidents($siteid)
  1070. {
  1071. global $dbSites, $db;
  1072. $sql = "UPDATE `{$dbSites}` SET freesupport = (freesupport - 1) WHERE id='{$siteid}'";
  1073. mysqli_query($db, $sql);
  1074. if (mysqli_affected_rows($db) < 1)
  1075. {
  1076. trigger_error("No rows affected while updating freesupport", E_USER_ERROR);
  1077. }
  1078. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_ERROR);
  1079. else return TRUE;
  1080. }
  1081. /**
  1082. * Increment the contract count of incidents used by one
  1083. * @param int $maintid - Contract ID
  1084. */
  1085. function increment_incidents_used($maintid)
  1086. {
  1087. global $dbMaintenance, $db;
  1088. $sql = "UPDATE `{$dbMaintenance}` SET incidents_used = (incidents_used + 1) WHERE id='{$maintid}'";
  1089. mysqli_query($db, $sql);
  1090. if (mysqli_affected_rows($db) < 1) trigger_error("No rows affected while updating freesupport", E_USER_ERROR);
  1091. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_ERROR);
  1092. else return TRUE;
  1093. }
  1094. /**
  1095. * Counts the number of incidents opened on a specific date
  1096. * @param int $day The day of the month
  1097. * @param int $month The month of the year
  1098. * @param int $year The year
  1099. * @author Ivan Lucas
  1100. */
  1101. function countdayincidents($day, $month, $year)
  1102. {
  1103. global $dbIncidents, $db;
  1104. $unixstartdate = mktime(0, 0, 0, $month, $day, $year);
  1105. $unixenddate = mktime(23, 59, 59, $month, $day, $year);
  1106. $sql = "SELECT count(id) FROM `{$dbIncidents}` ";
  1107. $sql .= "WHERE opened BETWEEN '{$unixstartdate}' AND '{$unixenddate}' ";
  1108. $result = mysqli_query($db, $sql);
  1109. list($count) = mysqli_fetch_row($result);
  1110. mysqli_free_result($result);
  1111. return $count;
  1112. }
  1113. /**
  1114. * Counts the number of incidents closed on a specific date
  1115. * @param int $day The day of the month
  1116. * @param int $month The month of the year
  1117. * @param int $year The year
  1118. * @author Ivan Lucas
  1119. */
  1120. function countdayclosedincidents($day, $month, $year)
  1121. {
  1122. global $dbIncidents, $db;
  1123. $unixstartdate = mktime(0, 0, 0, $month, $day, $year);
  1124. $unixenddate = mktime(23, 59, 59, $month, $day, $year);
  1125. $sql = "SELECT COUNT(id) FROM `{$dbIncidents}` ";
  1126. $sql .= "WHERE closed BETWEEN '{$unixstartdate}' AND '{$unixenddate}' ";
  1127. $result = mysqli_query($db, $sql);
  1128. list($count) = mysqli_fetch_row($result);
  1129. mysqli_free_result($result);
  1130. return $count;
  1131. }
  1132. /**
  1133. * Counts the number of incidents open on a specific date
  1134. * @param int $day The day of the month
  1135. * @param int $month The month of the year
  1136. * @param int $year The year
  1137. * @author Ivan Lucas
  1138. */
  1139. function countdaycurrentincidents($day, $month, $year)
  1140. {
  1141. global $dbIncidents, $db;
  1142. $unixstartdate = mktime(0, 0, 0, $month, $day, $year);
  1143. $unixenddate = mktime(23, 59, 59, $month, $day, $year);
  1144. $sql = "SELECT COUNT(id) FROM `{$dbIncidents}` ";
  1145. $sql .= "WHERE opened <= '{$unixenddate}' AND closed >= '{$unixstartdate}' ";
  1146. $result = mysqli_query($db, $sql);
  1147. list($count) = mysqli_fetch_row($result);
  1148. mysqli_free_result($result);
  1149. return $count;
  1150. }
  1151. /**
  1152. * Counts the number of updates not yet linked to an incident
  1153. * @author Ivan Lucas
  1154. * @note Updates are linked to incident ID 0 (no such incident) when they
  1155. * arrive via email or portal and not yet assigned to an incident.
  1156. */
  1157. function count_incoming_updates()
  1158. {
  1159. global $db;
  1160. $sql = "SELECT id FROM `{$GLOBALS['dbUpdates']}` WHERE incidentid=0";
  1161. $result = mysqli_query($db, $sql);
  1162. $count = mysqli_num_rows($result);
  1163. mysqli_free_result($result);
  1164. return $count;
  1165. }
  1166. /**
  1167. * Identify the next due SLA target for a given incident
  1168. *
  1169. * @param int $incidentid
  1170. * retval string Target type
  1171. */
  1172. function incident_get_next_target($incidentid)
  1173. {
  1174. global $now, $db;
  1175. // Find the most recent SLA target that was met
  1176. $sql = "SELECT sla, timestamp FROM `{$GLOBALS['dbUpdates']}` WHERE incidentid='{$incidentid}' AND sla IS NOT Null ORDER BY id DESC LIMIT 1";
  1177. $result = mysqli_query($db, $sql);
  1178. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  1179. $sla_targets = get_incident_sla_targets($incidentid);
  1180. $target = new SLATarget();
  1181. if (mysqli_num_rows($result) > 0)
  1182. {
  1183. $upd = mysqli_fetch_object($result);
  1184. switch ($upd->sla)
  1185. {
  1186. case 'opened':
  1187. if ($sla_targets->initial_response_mins > 0)
  1188. {
  1189. $target->type = 'initialresponse';
  1190. break;
  1191. }
  1192. case 'initialresponse':
  1193. if ($sla_targets->prob_determ_mins > 0)
  1194. {
  1195. $target->type = 'probdef';
  1196. break;
  1197. }
  1198. case 'probdef':
  1199. if ($sla_targets->action_plan_mins > 0)
  1200. {
  1201. $target->type = 'actionplan';
  1202. break;
  1203. }
  1204. case 'actionplan':
  1205. if ($sla_targets->resolution_days > 0)
  1206. {
  1207. $target->type = 'solution';
  1208. break;
  1209. }
  1210. case 'solution':
  1211. $target->type = '';
  1212. break;
  1213. case 'closed':
  1214. $target->type = 'opened';
  1215. break;
  1216. }
  1217. $target->since = calculate_incident_working_time($incidentid, $upd->timestamp, $now);
  1218. }
  1219. else
  1220. {
  1221. $target->type = 'regularcontact';
  1222. $target->since = 0;
  1223. }
  1224. return $target;
  1225. }
  1226. /**
  1227. * Convert an SLA target type to a readable SLA target name
  1228. * @param string $targettype
  1229. * @retval string Target type readable name
  1230. */
  1231. function target_type_name($targettype)
  1232. {
  1233. switch ($targettype)
  1234. {
  1235. case 'opened':
  1236. $name = $GLOBALS['strOpened'];
  1237. break;
  1238. case 'initialresponse':
  1239. $name = $GLOBALS['strInitialResponse'];
  1240. break;
  1241. case 'probdef':
  1242. $name = $GLOBALS['strProblemDefinition'];
  1243. break;
  1244. case 'actionplan':
  1245. $name = $GLOBALS['strActionPlan'];
  1246. break;
  1247. case 'solution':
  1248. $name = $GLOBALS['strResolutionReprioritisation'];
  1249. break;
  1250. case 'closed':
  1251. $name = '';
  1252. break;
  1253. case 'regularcontact':
  1254. $name = '';
  1255. break; // Contact Customer
  1256. default:
  1257. $name = '';
  1258. break;
  1259. }
  1260. return $name;
  1261. }
  1262. /**
  1263. * Returns the number of minutes since the last incident review for a specified
  1264. * incident
  1265. * @author Ivan Lucas
  1266. * @param int $incidentid - Incident ID
  1267. * @return int Time since the last review in minutes
  1268. * @note was called incident_get_next_review() (very bad name) until 3.60 14Mar10
  1269. */
  1270. function incident_time_since_review($incidentid)
  1271. {
  1272. global $now, $db;
  1273. $sql = "SELECT timestamp FROM `{$GLOBALS['dbUpdates']}` ";
  1274. $sql .= "WHERE incidentid='{$incidentid}' AND type='reviewmet' ";
  1275. $sql .= "ORDER BY id DESC LIMIT 1";
  1276. $result = mysqli_query($db, $sql);
  1277. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  1278. if (mysqli_num_rows($result) > 0)
  1279. {
  1280. $upd = mysqli_fetch_object($result);
  1281. $timesincereview = floor(($now - $upd->timestamp) / 60);
  1282. }
  1283. return $timesincereview;
  1284. }
  1285. /**
  1286. * Switches incidents temporary owners to the backup/substitute engineer depending on the setting of 'accepting'
  1287. * @author Ivan Lucas
  1288. * @param int $userid. The userid of the user who's status has changed.
  1289. * @param string $accepting. 'yes' or 'no' to indicate whether the user is accepting
  1290. * @note if the $accepting parameter is 'no' then the function will attempt to temporarily assign
  1291. * all the open incidents that the user owns to the users defined substitute engineers
  1292. * If Substitute engineers cannot be found or they themselves are not accepting, the given users incidents
  1293. * are placed in the holding queue
  1294. */
  1295. function incident_backup_switchover($userid, $accepting)
  1296. {
  1297. global $now, $dbIncidents, $dbUpdates, $dbTempAssigns, $dbUsers, $dbUserStatus, $db;
  1298. $usersql = "SELECT u.*, us.name AS statusname ";
  1299. $usersql .= "FROM `{$dbUsers}` AS u, `{$dbUserStatus}` AS us ";
  1300. $usersql .= "WHERE u.id = '{$userid}' AND u.status = us.id";
  1301. $userresult = mysqli_query($db, $usersql);
  1302. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  1303. $user = mysqli_fetch_row($userresult);
  1304. if (strtolower($accepting) == 'no')
  1305. {
  1306. // Look through the incidents that this user OWNS (and are not closed)
  1307. $sql = "SELECT * FROM `{$dbIncidents}` WHERE (owner='{$userid}' OR towner='{$userid}') AND status!=2";
  1308. $result = mysqli_query($db, $sql);
  1309. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  1310. while ($incident = mysqli_fetch_object($result))
  1311. {
  1312. // Try and find a backup/substitute engineer
  1313. $backupid = software_backup_userid($userid, $incident->softwareid);
  1314. if (empty($backupid) OR user_accepting($backupid) == 'No')
  1315. {
  1316. // no backup engineer found so add to the holding queue
  1317. // Look to see if this assignment is in the queue already
  1318. $fsql = "SELECT * FROM `{$dbTempAssigns}` WHERE incidentid='{$incident->id}' AND originalowner='{$userid}'";
  1319. $fresult = mysqli_query($db, $fsql);
  1320. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  1321. if (mysqli_num_rows($fresult) < 1)
  1322. {
  1323. // it's not in the queue, and the user isn't accepting so add it
  1324. //$userstatus=user_status($userid);
  1325. $userstatus = $user['status'];
  1326. $usql = "INSERT INTO `{$dbTempAssigns}` (incidentid,originalowner,userstatus) VALUES ('{$incident->id}', '{$userid}', '{$userstatus}')";
  1327. mysqli_query($db, $usql);
  1328. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_ERROR);
  1329. }
  1330. }
  1331. else
  1332. {
  1333. // do an automatic temporary reassign
  1334. // update incident
  1335. $rusql = "UPDATE `{$dbIncidents}` SET ";
  1336. $rusql .= "towner='{$backupid}', lastupdated='{$now}' WHERE id='{$incident->id}' LIMIT 1";
  1337. mysqli_query($db, $rusql);
  1338. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_ERROR);
  1339. // add update
  1340. $username = user_realname($userid);
  1341. //$userstatus = userstatus_name(user_status($userid));
  1342. $userstatus = $user['statusname'];
  1343. //$usermessage=user_message($userid);
  1344. $usermessage = $user['message'];
  1345. $bodytext = "Previous Incident Owner ({$username}) {$userstatus} {$usermessage}";
  1346. $assigntype = 'tempassigning';
  1347. $risql = "INSERT INTO `{$dbUpdates}` (incidentid, userid, bodytext, type, timestamp, currentowner, currentstatus) ";
  1348. $risql .= "VALUES ('{$incident->id}', '0', '{$bodytext}', '{$assigntype}', '{$now}', ";
  1349. $risql .= "'{$backupid}', ";
  1350. $risql .= "'{$incident->status}')";
  1351. mysqli_query($db, $risql);
  1352. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_ERROR);
  1353. // Look to see if this assignment is in the queue already
  1354. $fsql = "SELECT * FROM `{$dbTempAssigns}` WHERE incidentid='{$incident->id}' AND originalowner='{$userid}'";
  1355. $fresult = mysqli_query($db, $fsql);
  1356. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  1357. if (mysqli_num_rows($fresult) < 1)
  1358. {
  1359. //$userstatus=user_status($userid);
  1360. $userstatus = $user['status'];
  1361. $usql = "INSERT INTO `{$dbTempAssigns}` (incidentid,originalowner,userstatus,assigned) VALUES ('{$incident->id}', '{$userid}', '$userstatus','yes')";
  1362. mysqli_query($db, $usql);
  1363. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_ERROR);
  1364. }
  1365. else
  1366. {
  1367. // mark the temp assigns table so it's not showing in the holding queue
  1368. $tasql = "UPDATE `{$dbTempAssigns}` SET assigned='yes' WHERE originalowner='{$userid}' AND incidentid='{$incident->id}' LIMIT 1";
  1369. mysqli_query($db, $tasql);
  1370. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_ERROR);
  1371. }
  1372. }
  1373. }
  1374. }
  1375. elseif ($accepting == '')
  1376. {
  1377. // Do nothing when accepting status doesn't exist
  1378. }
  1379. else
  1380. {
  1381. // The user is now ACCEPTING, so first have a look to see if there are any reassignments in the queue
  1382. $sql = "SELECT * FROM `{$dbTempAssigns}` WHERE originalowner='{$userid}' ";
  1383. $result = mysqli_query($db, $sql);
  1384. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  1385. while ($assign = mysqli_fetch_object($result))
  1386. {
  1387. if ($assign->assigned == 'yes')
  1388. {
  1389. // Incident has actually been reassigned, so have a look if we can grab it back.
  1390. $lsql = "SELECT id,status FROM `{$dbIncidents}` ";
  1391. $lsql .= "WHERE id='{$assign->incidentid}' AND owner='{$assign->originalowner}' AND towner!=''";
  1392. $lresult = mysqli_query($db, $lsql);
  1393. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  1394. while ($incident = mysqli_fetch_object($lresult))
  1395. {
  1396. // Find our tempassign
  1397. $usql = "SELECT id,currentowner FROM `{$dbUpdates}` ";
  1398. $usql .= "WHERE incidentid='{$incident->id}' AND userid='0' AND type='tempassigning' ";
  1399. $usql .= "ORDER BY id DESC LIMIT 1";
  1400. $uresult = mysqli_query($db, $usql);
  1401. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  1402. list($prevassignid,$tempowner) = mysqli_fetch_row($uresult);
  1403. // Look to see if the temporary owner has updated the incident since we temp assigned it
  1404. // If he has, we leave it in his queue
  1405. $usql = "SELECT id FROM `{$dbUpdates}` ";
  1406. $usql .= "WHERE incidentid='{$incident->id}' AND id > '{$prevassignid}' AND userid='{$tempowner}' LIMIT 1 ";
  1407. $uresult = mysqli_query($db, $usql);
  1408. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  1409. if (mysqli_num_rows($uresult) < 1)
  1410. {
  1411. // Incident appears not to have been updated by the temporary owner so automatically reassign back to orignal owner
  1412. // update incident
  1413. $rusql = "UPDATE `{$dbIncidents}` SET ";
  1414. $rusql .= "towner='', lastupdated='{$now}' WHERE id='{$incident->id}' LIMIT 1";
  1415. mysqli_query($db, $rusql);
  1416. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_ERROR);
  1417. // add update
  1418. $username = user_realname($userid);
  1419. //$userstatus = userstatus_name(user_status($userid));
  1420. $userstatus = $user['statusname'];
  1421. //$usermessage=user_message($userid);
  1422. $usermessage = $user['message'];
  1423. $bodytext = "Reassigning to original owner {$username} ({$userstatus})";
  1424. $assigntype = 'reassigning';
  1425. $risql = "INSERT INTO `{$dbUpdates}` (incidentid, userid, bodytext, type, timestamp, currentowner, currentstatus) ";
  1426. $risql .= "VALUES ('{$incident->id}', '0', '{$bodytext}', '{$assigntype}', '{$now}', ";
  1427. $risql .= "'{$backupid}', ";
  1428. $risql .= "'{$incident->status}')";
  1429. mysqli_query($db, $risql);
  1430. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_ERROR);
  1431. // remove from assign queue now, all done
  1432. $rsql = "DELETE FROM `{$dbTempAssigns}` WHERE incidentid='{$assign->incidentid}' AND originalowner='{$assign->originalowner}'";
  1433. mysqli_query($db, $rsql);
  1434. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_ERROR);
  1435. }
  1436. }
  1437. }
  1438. else
  1439. {
  1440. // now have a look to see if the reassign was completed
  1441. $ssql = "SELECT id FROM `{$dbIncidents}` WHERE id='{$assign->incidentid}' LIMIT 1";
  1442. $sresult = mysqli_query($db, $ssql);
  1443. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  1444. if (mysqli_num_rows($sresult) >= 1)
  1445. {
  1446. // reassign wasn't completed, or it was already assigned back, simply remove from assign queue
  1447. $rsql = "DELETE FROM `{$dbTempAssigns}` WHERE incidentid='{$assign->incidentid}' AND originalowner='{$assign->originalowner}'";
  1448. mysqli_query($db, $rsql);
  1449. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_ERROR);
  1450. }
  1451. }
  1452. }
  1453. }
  1454. return;
  1455. }
  1456. /**
  1457. * Inserts a new incident update
  1458. * @param int $incidentid ID of the incident to add the update to
  1459. * @param string $text The text of the update
  1460. * @param enum $type (Optional) Update type (Default: 'default'), types:
  1461. * 'default', 'editing', 'opening', 'email', 'reassigning', 'closing',
  1462. * 'reopening', 'auto', 'phonecallout', 'phonecallin', 'research', 'webupdate',
  1463. * 'emailout', 'emailin', 'externalinfo', 'probdef', 'solution', 'actionplan',
  1464. * 'slamet', 'reviewmet', 'tempassigning', 'auto_chase_email',
  1465. * 'auto_chase_phone', 'auto_chase_manager', 'auto_chased_phone',
  1466. * 'auto_chased_manager', 'auto_chase_managers_manager',
  1467. * 'customerclosurerequest', 'fromtask'
  1468. * @param string $sla The SLA the update meets
  1469. * @param int $userid (Optional) ID of the user doing the updating (Default: 0)
  1470. * @param int $currentowner (Optional) ID of the current incident owner
  1471. * @param int $currentstatus (Optional) Current incident status (Default: 1 = active)
  1472. * @param enum $visibility (Optional) Whether to 'show' or 'hide' in the portal (Default: 'show')
  1473. * @author Kieran Hogg
  1474. */
  1475. function new_update($incidentid, $text, $type = 'default', $sla = '', $userid = 0, $currentowner = '',
  1476. $currentstatus = 1, $visibility = 'show')
  1477. {
  1478. global $now, $db;
  1479. $sql = "INSERT INTO `{$GLOBALS['dbUpdates']}` (incidentid, userid, ";
  1480. $sql .= "type, bodytext, timestamp, currentowner, currentstatus, ";
  1481. $sql .= "customervisibility, sla) VALUES ('{$incidentid}', '{$userid}', ";
  1482. $sql .= "'{$type}', '{$text}', '{$now}', '{$currentowner}', ";
  1483. $sql .= "'{$currentstatus}', '{$visibility}', '{$sla}')";
  1484. $result = mysqli_query($db, $sql);
  1485. if (mysqli_error($db))
  1486. {
  1487. trigger_error("MySQL Query Error ".mysqli_error($db), E_USER_ERROR);
  1488. return FALSE;
  1489. }
  1490. else
  1491. {
  1492. return mysqli_insert_id($db);
  1493. }
  1494. }
  1495. /**
  1496. * Create a new holding queue item
  1497. * @param int $updateid ID of the associated update entry
  1498. * @param string $from Name of the from field
  1499. * @param string $subject Subject of the item
  1500. * @param string $emailfrom Email address the item is from
  1501. * @param int $contactid (Optional) Contact ID of the sender
  1502. * @param int $incidentid (Optional) Associated incident ID
  1503. * @param int $locked (Optional) 1 if the item is locked, 0 if not
  1504. * @param time $lockeduntil (Optional) MySQL timestamp of lock expiration
  1505. * @param string $reason (Optional) Reason the item is in the holding queue
  1506. * @param id $reason_user (Optional) The user ID who set the reason
  1507. * @param time $reason_time (Optional) MySQL timestamp of when the reason was set
  1508. * @author Kieran Hogg
  1509. */
  1510. function create_temp_incoming($updateid, $from, $subject, $emailfrom,
  1511. $contactid = '', $incidentid = 0, $locked = '',
  1512. $lockeduntil = '', $reason = '',
  1513. $reason_user = '', $reason_time = '')
  1514. {
  1515. global $dbTempIncoming, $db;
  1516. $sql = "INSERT INTO `{$dbTempIncoming}`(updateid, `from`, subject, ";
  1517. $sql .= "emailfrom, contactid, incidentid, locked, lockeduntil, ";
  1518. $sql .= "reason, reason_user, reason_time) VALUES('{$updateid}', ";
  1519. $sql .= "'{$from}', '{$subject}', '{$emailfrom}', '{$contactid}', ";
  1520. $sql .= "'{$incidentid}', '{$locked}', '{$lockeduntil}', '{$reason}', ";
  1521. $sql .= "'{$reason_user}', '{$reason_time}')";
  1522. $result = mysqli_query($db, $sql);
  1523. if (mysqli_error($db))
  1524. {
  1525. trigger_error("MySQL Query Error ".mysqli_error($db), E_USER_ERROR);
  1526. return FALSE;
  1527. }
  1528. else
  1529. {
  1530. return mysqli_insert_id($db);
  1531. }
  1532. }
  1533. function holding_email_update_id($holding_email)
  1534. {
  1535. $holding_email = intval($holding_email);
  1536. return db_read_column('updateid', $GLOBALS['dbTempIncoming'], $holding_email);
  1537. }
  1538. function delete_holding_queue_update($updateid)
  1539. {
  1540. global $db;
  1541. $sql = "DELETE FROM `{$GLOBALS['dbTempIncoming']}` WHERE updateid = '{$updateid}'";
  1542. mysqli_query($db, $sql);
  1543. if (mysqli_error($db))
  1544. {
  1545. trigger_error(mysqli_error($db). " {$sql}", E_USER_WARNING);
  1546. return FALSE;
  1547. }
  1548. else
  1549. {
  1550. return TRUE;
  1551. }
  1552. }
  1553. function num_unread_emails()
  1554. {
  1555. global $dbTempIncoming, $db;
  1556. $sql = "SELECT COUNT(*) AS count FROM `{$dbTempIncoming}`";
  1557. $result = mysqli_query($db, $sql);
  1558. if (mysqli_error($db)) trigger_error(mysqli_error($db). " {$sql}", E_USER_WARNING);
  1559. list($count) = mysqli_fetch_row($result);
  1560. return $count;
  1561. }
  1562. /**
  1563. * Return the number of incidents ever logged against a site
  1564. * @author Kieran
  1565. * @param int $id. Site ID
  1566. * @param boolean $open. Include only open incidents (FALSE includes all)
  1567. * @return int.
  1568. */
  1569. function site_count_incidents($id, $open=FALSE)
  1570. {
  1571. global $dbIncidents, $dbContacts, $db;
  1572. $id = intval($id);
  1573. $count = 0;
  1574. $sql = "SELECT COUNT(i.id) FROM `{$dbIncidents}` AS i, `{$dbContacts}` as c ";
  1575. $sql .= "WHERE i.contact = c.id ";
  1576. $sql .= "AND c.siteid='{$id}' ";
  1577. if ($open) $sql .= "AND i.status != ".STATUS_CLOSED;
  1578. $result = mysqli_query($db, $sql);
  1579. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  1580. else list($count) = mysqli_fetch_row($result);
  1581. mysqli_free_result($result);
  1582. return $count;
  1583. }
  1584. /**
  1585. * @author Paul Heaney
  1586. */
  1587. function display_drafts($type, $result)
  1588. {
  1589. global $iconset;
  1590. global $id;
  1591. global $CONFIG;
  1592. if ($type == 'update')
  1593. {
  1594. $page = "incident_update.php";
  1595. $editurlspecific = '';
  1596. }
  1597. else if ($type == 'email')
  1598. {
  1599. $page = "incident_email.php";
  1600. $editurlspecific = "&amp;step=2";
  1601. }
  1602. echo "<p align='center'>{$GLOBALS['strDraftChoose']}</p>";
  1603. $html = '';
  1604. while ($obj = mysqli_fetch_object($result))
  1605. {
  1606. $html .= "<div class='detailhead'>";
  1607. $html .= "<div class='detaildate'>".date($CONFIG['dateformat_datetime'], $obj->lastupdate);
  1608. $html .= "</div>";
  1609. $html .= "<a href='{$page}?action=editdraft&amp;draftid={$obj->id}&amp;id={$id}{$editurlspecific}' class='info'>";
  1610. $html .= icon('edit', 16, $GLOBALS['strDraftEdit'])."</a>";
  1611. $html .= "<a href='{$page}?action=deletedraft&amp;draftid={$obj->id}&amp;id={$id}' class='info'>";
  1612. $html .= icon('delete', 16, $GLOBALS['strDraftDelete'])."</a>";
  1613. $html .= "</div>";
  1614. $html .= "<div class='detailentry'>";
  1615. $html .= nl2br($obj->content)."</div>";
  1616. }
  1617. return $html;
  1618. }
  1619. function external_escalation($escalated, $incid)
  1620. {
  1621. foreach ($escalated as $i => $id)
  1622. {
  1623. if ($id == $incid)
  1624. {
  1625. return "yes";
  1626. }
  1627. }
  1628. return "no";
  1629. }
  1630. /**
  1631. * Get an SLA of an incident in human-readable format
  1632. * Written for trigger templates but can be re-used
  1633. * @param int $incident_id. Incident ID
  1634. * @param string $type. Type of the SLA, from: initial_response, prob_determ, action_plan, resolution
  1635. * @return string
  1636. */
  1637. function incident_sla($incident_id, $type)
  1638. {
  1639. global $dbServiceLevels, $db;
  1640. $incident = incident($incident_id);
  1641. $sql = "SELECT * FROM `{$dbServiceLevels}` ";
  1642. $sql .= "WHERE tag = '{$incident->servicelevel}' AND priority = '{$incident->priority}'";
  1643. $result = mysqli_query($db, $sql);
  1644. if (mysqli_error($db))
  1645. {
  1646. trigger_error("MySQL Query Error ".mysqli_error($db), E_USER_ERROR);
  1647. return "Error getting SLA";
  1648. }
  1649. else
  1650. {
  1651. $sla_obj = mysqli_fetch_object($result);
  1652. switch ($type)
  1653. {
  1654. case 'prob_determ':
  1655. $sla = $sla_obj->prob_determ_mins;
  1656. break;
  1657. case 'action_plan':
  1658. $sla = $sla_obj->action_plan_mins;
  1659. break;
  1660. case 'resolution':
  1661. $sla = $sla_obj->resolution_days * 480;
  1662. break;
  1663. case 'initial_response':
  1664. default:
  1665. $sla = $sla_obj->initial_response_mins;
  1666. break;
  1667. }
  1668. return format_workday_minutes($sla);
  1669. }
  1670. }
  1671. /**
  1672. * Get the user facing incident ID, this translates the internal ID to the user ID shown to the user
  1673. * @author Paul Heaney
  1674. * @param int $id The internal incident ID (from the database)
  1675. * @return String The user facing incident ID
  1676. */
  1677. function get_userfacing_incident_id($id)
  1678. {
  1679. global $CONFIG;
  1680. return "{$CONFIG['incident_reference_prefix']}{$id}";
  1681. }
  1682. /**
  1683. * Get the user facing incident ID used for emails, this translates the internal ID to the user ID shown to the user
  1684. * @author Paul Heaney
  1685. * @param int $id The internal incident ID (from the database)
  1686. * @return String The user facing incident ID for emails
  1687. */
  1688. function get_userfacing_incident_id_email($id)
  1689. {
  1690. global $CONFIG;
  1691. return "{$CONFIG['incident_id_email_opening_tag']}{$CONFIG['incident_reference_prefix']}{$id}{$CONFIG['incident_id_email_closing_tag']}";
  1692. }
  1693. /**
  1694. * Gets all the SLA targets for a particular incident
  1695. *
  1696. * @author Paul Heaney..
  1697. * @param int $incidentid The ID of the incident to get the applicable SLA targets for.
  1698. * @return object With the following properties: initial_response_mins, prob_determ_mins, action_plan_mins, resolution_days, review_days, timed, allow_reopen
  1699. */
  1700. function get_incident_sla_targets($incidentid)
  1701. {
  1702. global $db;
  1703. $incidentsla = incident_service_level($incidentid);
  1704. $sql_sla = "SELECT initial_response_mins, prob_determ_mins, action_plan_mins, resolution_days, review_days, timed, allow_reopen ";
  1705. $sql_sla .= "FROM `{$GLOBALS['dbServiceLevels']}` WHERE tag = '{$incidentsla}' GROUP BY tag";
  1706. $result_sla = mysqli_query($db, $sql_sla);
  1707. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  1708. return mysqli_fetch_object($result_sla);
  1709. }
  1710. /**
  1711. * Not 100% sure of the purpose of this function though triggers requires it, TODO revisit and fully under stand
  1712. *
  1713. * @author Paul Heaney - to fix Mantis 1372 (lack of function)
  1714. * @param int $holdingemailid The holding queue ID
  1715. * @return int the update ID
  1716. */
  1717. function incoming_email_update_id($holdingemailid)
  1718. {
  1719. global $db;
  1720. $sql = "SELECT updateid FROM `{$GLOBALS['dbTempIncoming']}` WHERE id = {$holdingemailid}";
  1721. $contractresult = mysqli_query($db, $sql);
  1722. if (mysqli_error($db)) trigger_error(mysqli_error($db), E_USER_WARNING);
  1723. list($updateid) = mysqli_fetch_array($result);
  1724. return $updateid;
  1725. }
  1726. ?>