runreport.php
上传用户:gzy2002
上传日期:2010-02-11
资源大小:1785k
文件大小:59k
源码类别:

电子政务应用

开发平台:

Java

  1. <?php
  2. // +-------------------------------------------------------------+
  3. // | DeskPRO v [2.0.1 Production]
  4. // | Copyright (C) 2001 - 2004 Headstart Solutions Limited
  5. // | Supplied by WTN-WDYL
  6. // | Nullified by WTN-WDYL
  7. // | Distribution via WebForum, ForumRU and associated file dumps
  8. // +-------------------------------------------------------------+
  9. // | DESKPRO IS NOT FREE SOFTWARE
  10. // +-------------------------------------------------------------+
  11. // | License ID : Full Enterprise License =) ...
  12. // | License Owner : WTN-WDYL Team
  13. // +-------------------------------------------------------------+
  14. // | $RCSfile: runreport.php,v $
  15. // | $Date: 2004/02/10 01:34:25 $
  16. // | $Revision: 1.128 $
  17. // +-------------------------------------------------------------+
  18. // | File Details:
  19. // | - Report generator (administration interface)
  20. // +-------------------------------------------------------------+
  21. error_reporting(E_ALL & ~E_NOTICE);
  22. require_once('./global.php');
  23. // License checks
  24. //Nullify WTN-WDYL Team
  25. feature_check('reports');
  26. // Stop JPgraph from installing an error handler here, since it causes problems.
  27. define('NO_GRAPHIC_HANDLER', 'true');
  28. // Need the calendar functions here for date slinging.
  29. include "./../includes/functions/calendar_functions.php";
  30. // default do
  31. $_REQUEST['do'] = trim($_REQUEST['do']);
  32. if (!isset($_REQUEST['do']) or $_REQUEST['do'] == "") {
  33. $_REQUEST['do'] = "runreport";
  34. }
  35. // globalise variables
  36. $global = array (
  37. array('id', 'number')
  38. );
  39. rg($global);
  40. // MODE: previewstat
  41. // If the administrator has requested a preview of a statistic, 
  42. // we generate it here. The reporting engine runs 365 days into
  43. // the past to gather statistics to show the preview (so this
  44. // doesn't always look as good as it should.
  45. if ($_REQUEST['do'] == 'previewstat') {
  46. $work[] = $db->query_return("SELECT * FROM report_stat WHERE id = '$id'");
  47. if (!$db->num_rows()) {
  48. mistake('No valid statistic specified.');
  49. exit;
  50. }
  51. $work[0]['title'] = "Statistic Preview ({$work[0][title]})";
  52. $style = array('id' => 1, 'name' => 'style', 'title_colour' => '', 'description_colour' => '');
  53. $report = array(
  54. 'id' => 1,
  55. 'title' => "{$work[0][title]}",
  56. 'name' => 'None',
  57. 'description' => '',
  58. 'lastrun' => 0,
  59. 'format' => 'html',
  60. 'email' => '',
  61. 'repeattype' => '',
  62. 'value1' => '',
  63. 'value2' => '',
  64. 'style' => '1',
  65. 'path' => ''
  66. );
  67. $date = split('-', strtotime(date('Y-m-d').' - 1 year'));
  68. $_REQUEST['ydate'] = $date[0];
  69. $_REQUEST['mdate'] = $date[1];
  70. $_REQUEST['ddate'] = $date[2];
  71. $_REQUEST['number'] = 1;
  72. $_REQUEST['type'] = 'years';
  73. if ($work['displaytype'] == 'csv') {
  74. $csv = 1;
  75. }
  76. } else {
  77. // MODE: regular report
  78. // If we're *not* generating a preview, we're actually running the
  79. // report instead. Run through the given range of time and gather
  80. // statistics.
  81. $report = $db->query_return("SELECT * FROM report WHERE id = '$id'");
  82. $style = $db->query_return("SELECT * FROM report_style WHERE id = '$report[style]'");
  83. $db->query("
  84. SELECT report_stat.*
  85. FROM report_relations
  86. LEFT JOIN report_stat ON (report_stat.id = report_relations.statid)
  87. WHERE reportid = '$id'
  88. ");
  89. $work = array();
  90. while ($stat = $db->row_array()) {
  91. $work[] = $stat;
  92. if ($stat['displaytype'] == 'csv') {
  93. $csv = 1;
  94. }
  95. }
  96. }
  97. // CSV statistics must be standalone; if even one statistic in a report is a
  98. // CSV, we have to ignore the rest and just process the CSV.
  99. if ($csv) { 
  100. $csv = array();
  101. foreach ($work as $stat) {
  102. if ($stat['displaytype'] == 'csv') {
  103. $csv[] = $stat;
  104. }
  105. }
  106. $work = array($csv[0]);
  107. $report['format'] = 'html';
  108. }
  109. // If the report's format is PDF, set up the PDF object and prepare it for
  110. // data.
  111. if ($report['format'] == 'PDF') {
  112. define('FPDF_FONTPATH','../includes/fpdf/font/');
  113. require('../includes/fpdf/fpdf.php');
  114. $pdf = new FPDF('P', 'mm', 'Letter');
  115. $pdf->Open();
  116. $pdf->AddPage();
  117. if ($report[title]) {
  118. $pdf->SetTitle($report['title']);
  119. $pdf->SetCreator('DeskPro v2.0');
  120. $pdf->SetFont('Times', 'B', 24);
  121. $pdf->Cell(0, 10, $report['title'], 0, 1, 'C');
  122. }
  123. }
  124. // If the report is to be mailed or saved, validate the inputs and set up if
  125. // needed.
  126. if ($report['email'] or $report['path']) { // Check for save-to-disk or e-mail options
  127. if (!validate_email($report['email'])) { // We've got a valid e-mail address
  128. $ext_error = 'The e-mail address specified, ' . htmlspecialchars_uni($report['email']) . ', is not valid. Not mailing.';
  129. $report['email'] = '';
  130. }
  131. if ($report['path']) { // Permissions checks for writing report to file.
  132. $filename = htmlspecialchars_uni($report['path']);
  133. if (is_dir($report['path'])) { // If it's a directory, we need to make up a name and try creating the file
  134. $tmpname = tempnam ($report['path'], 'report');
  135. if ($report['format'] == 'html') {
  136. $tmpname .= '.html';
  137. } else {
  138. $tmpname .= '.pdf';
  139. }
  140. $filename = htmlspecialchars_uni($tmpname);
  141. $handle = fopen($tmpfname, "wb");
  142. if (!$handle) {
  143. $ext_error = "Cannot create $filename.";
  144. $report['path'] = '';
  145. }
  146. } elseif (file_exists($report['path'])) { // Else, if it's a file, it'd best not exist yet.
  147. $ext_error = 'The filename you specified, ' . htmlspecialchars_uni($report['path']) . ', already exists.';
  148. $report['path'] = '';
  149. } else {
  150. $handle = @fopen($report['path'], 'wb');
  151. if (!$handle) {
  152. $ext_error = 'Cannot open ' . htmlspecialchars_uni($report['path']);
  153. $report['path'] = '';
  154. }
  155. if (!$handle) {
  156. $ext_error = "Cannot create $filename; make sure DeskPRO has permission to create files in this location.";
  157. $report['path'] = '';
  158. }
  159. }
  160. }
  161. }
  162. // Same check, different actions if they're still true now that we've validated some things.
  163. if ($report['email'] or $report['path']) { // Do we need to save this output to a buffer for later handling?
  164. $buffer = 1;
  165. ob_start();
  166. } elseif ($ext_error) {
  167. $report['format'] = 'html';
  168. print "<B>WARNING:</B> $ext_error Producing HTML output<BR />n";
  169. }
  170. // Each statistic is run independently of any others in a report. They are
  171. // processed in the order the database query returns them in. This loop runs
  172. // once for each statistic.
  173. foreach ($work as $stat) {
  174. unset($appendix);
  175. unset($data);
  176. unset($data2);
  177. unset($data_tmp);
  178. unset($data_tmp2);
  179. unset($where);
  180. unset($daterange);
  181. $interval = 0;
  182. // Unserialize data fields if any are present.
  183. if (isset($stat['fixed_general'])) {
  184. $fixed_general = unserialize($stat['fixed_general']);
  185. }
  186. if (isset($stat['fixed_user'])) {
  187. $fixed_user = unserialize($stat['fixed_user']);
  188. }
  189. if (isset($stat['fixed_ticket'])) {
  190. $fixed_ticket = unserialize($stat['fixed_ticket']);
  191. }
  192. // =========== Begin building the first level query ============
  193. $where = '1 ';
  194. ################# FIXED GENERAL FIELDS ################# 
  195. // open / closed
  196. if ($fixed_general['open_status'] == 'open') {
  197. $where .= " AND is_open = 1";
  198. } elseif ($fixed_general['open_status'] == 'closed') {
  199. $where .= " AND is_open = 0";
  200. }
  201. // awaiting tech / user
  202. if (isset($fixed_general['awaiting_status'])) {
  203. if ($fixed_general['awaiting_status'] == 'tech') {
  204. $where .= " AND awaiting_tech";
  205. } elseif ($fixed_general['awaiting_status'] == 'user') {
  206. $where .= " AND !awaiting_tech";
  207. }
  208. }
  209. // category
  210. if (isset($fixed_general['category'])) {
  211. if (is_array($fixed_general['category'])) {
  212. $where .= " AND category IN " . array2sql($fixed_general['category']);
  213. }
  214. }
  215. // priority
  216. if (isset($fixed_general['category'])) {
  217. if (is_array($fixed_general['priority'])) {
  218. $where .= " AND priority IN " . array2sql($fixed_general['priority']);
  219. }
  220. }
  221. ################# FIXED CUSTOM TICKET FIELDS ################# 
  222. // For each custom ticket field specified by the statistic, add the correct
  223. // search clause to the query.
  224. if (is_array($fixed_ticket)) {
  225. $values = array();
  226. $db->query("SELECT * FROM ticket_def");
  227. while ($result = $db->row_array()) {
  228. $display_name = unserialize($result['display_name']);
  229. $tfields_names[$result['name']] = $display_name[1];
  230. $tfields[$result['name']] = $result;
  231. $ticket_fields[$result['name']] = $result['formtype'];
  232. }
  233. foreach ($fixed_ticket AS $key => $var) {
  234. if (in_array($key, array_keys($ticket_fields)) AND $fixed_ticket[$key]) {
  235. $where .= field_search($tfields[$key], 
  236. $fixed_ticket[$key],
  237. $fixed_ticket["extra$key"],
  238. 'ticket',
  239. $fixed_ticket[$key."_match"],
  240. $fixed_ticket[$key."_not"]);
  241. }
  242. }
  243. }
  244. ################# FIXED CUSTOM USER FIELDS ################# 
  245. // For each custom user field specified by the statistic, add the correct
  246. // search clause to the query. This is done by running another query to
  247. // find all matching users, then using those results to add to the primary
  248. // query.
  249. if (is_array($fixed_user)) {
  250. $values = array();
  251. $db->query("SELECT * FROM user_def");
  252. while ($result = $db->row_array()) {
  253. $display_name = unserialize($result['display_name']);
  254. $ufields_names[$result['name']] = $display_name[1];
  255. $ufields[$result['name']] = $result;
  256. $user_fields[$result['name']] = $result['formtype'];
  257. }
  258. $where2 = '1';
  259. foreach ($fixed_user AS $key => $var) {
  260. if (in_array($key, array_keys($user_fields)) AND $fixed_user[$key]) {
  261. $where2 .= field_search($ufields[$key], 
  262. $fixed_user[$key],
  263. $fixed_user["extra$key"],
  264. 'user',
  265. $fixed_user[$key."_match"],
  266. $fixed_user[$key."_not"]);
  267. }
  268. }
  269. $db->query("SELECT user.id FROM user WHERE $where2");
  270. $techs = array();
  271. if ($db->num_rows()) {
  272. while ($result = $db->row_array()) {
  273. $techs[] = $result['id'];
  274. }
  275. $techs = join(',', $techs);
  276. $where .= " AND ticket.userid IN ($techs)";
  277. }
  278. }
  279. ################# DATE ################# 
  280. $startdate = formatymd("$_REQUEST[ydate]-$_REQUEST[mdate]-$_REQUEST[ddate]");
  281. if (validate_ymd($startdate) && $_REQUEST['number'] && $_REQUEST['type']) {
  282. $enddate = strtotime("$startdate + $_REQUEST[number] $_REQUEST[type]");
  283. $startdate = strtotime($startdate);
  284. $daterange = " AND date_opened >= '$startdate'
  285. AND date_opened < '$enddate' ";
  286. }
  287. ################# PRE PROCESSING (TO CHANGE INDEXES FROM NUMBERS TO VALUES) #################
  288. $change['is_open'] = array(
  289. 0 => 'Closed',
  290. 1 => 'Open'
  291. );
  292. $change['awaiting_tech'] = array(
  293. 0 => 'Awaiting User Response',
  294. 1 => 'Awaiting Technician Response'
  295. );
  296. // category
  297. $db->query("SELECT id, name FROM ticket_cat");
  298. while ($result = $db->row_array()) {
  299. $change['category'][$result['id']] = $result['name'];
  300. }
  301. $change['category'][0] = 'No category specified';
  302. // priority
  303. $db->query("SELECT id, name FROM ticket_pri");
  304. while ($result = $db->row_array()) {
  305. $change['priority'][$result['id']] = $result['name'];
  306. }
  307. $change['priority'][0] = 'No priority specified';
  308. // tech
  309. $db->query("SELECT id, username FROM tech");
  310. while ($result = $db->row_array()) {
  311. $change['tech'][$result['id']] = $result['username'];
  312. }
  313. $change['tech'][0] = 'Unassigned';
  314. // users
  315. $db->query("SELECT id, username FROM user");
  316. while ($result = $db->row_array()) {
  317. $change['userid'][$result['id']] = $result['username'];
  318. }
  319. $change['userid'][0] = 'Unowned';
  320. ################# 1ST VARIABLE PROCESSING #################
  321. // If this statistic has a Date Affects setting, then we're doing some
  322. // custom processing. 
  323. // If it's an "interval"-style setting, the intervals make up the "first
  324. // variable" for this statistic, and the real "1st Variable" becomes the
  325. // "2nd Variable"; we gather all the first-pass query results together to
  326. // let 2nd Variable processing handle it from there.
  327. // If it's a "length"-style setting, we're just searching for tickets that
  328. // match some function-of-time value and so can just be added to the WHERE
  329. // clause; we process those here.
  330. if ($stat['dateaffect'] != 'none') {
  331. // Build the intervals list (dividing the total report range into
  332. // sections of time as specified by the statistic
  333. $intervals = explode(',', $stat['variable1times']);
  334. $i_years = $intervals[0];
  335. $i_days  = $intervals[1];
  336. $i_hours = $intervals[2];
  337. $i_mins  = $intervals[3];
  338. if (!$i_hours AND !$i_mins) {
  339. $dateformat = 'm/d/Y';
  340. } elseif (!$i_years AND !$i_days) {
  341. $dateformat = 'h:i:sa';
  342. } else {
  343. $dateformat = 'm/d/Y h:i:sa';
  344. }
  345. $duration = ($i_mins * 60) + ($i_hours * 3600) + ($i_days * 86400) + ($i_years * 31536000);
  346. $intervals = get_intervals($startdate, $enddate,
  347. "+ $i_years years $i_days days $i_hours hours $i_mins minutes"
  348. );
  349. }
  350. switch ($stat['dateaffect']) {
  351. default: // Regular processing
  352. $db->query("SELECT COUNT(*) AS totals, $stat[variable1]
  353. FROM ticket
  354. WHERE $where $daterange
  355. GROUP BY ticket.$stat[variable1]
  356. ");
  357. while ($result = $db->row_array()) {
  358. $data_tmp[$result[$stat['variable1']]] = $result['totals'];
  359. }
  360. $db->query("SELECT ticket.id
  361. FROM ticket
  362. WHERE $where $daterange
  363. ");
  364. while ($result = $db->row_array()) {
  365. $appendix[$result['id']] = 1;
  366. }
  367. break;
  368. case 'opened': // Interval: all tickets opened between start and end,
  369.    // grouped by interval
  370. $stat['variable2'] = $stat['variable1'];
  371. $result = $db->query_return_array("SELECT ticket.id, ticket.$stat[variable1], ticket.date_opened
  372. FROM ticket
  373. WHERE $where
  374. AND date_opened >= '$startdate'
  375. AND date_opened < '$enddate'");
  376. foreach ($intervals as $intkey => $intval) {
  377. $data_tmp[date($dateformat, $intval[0])] = 0;
  378. foreach ($result AS $tmpdata) {
  379. if ($tmpdata['date_opened'] >= $intval[0] AND $tmpdata['date_opened'] < $intval[1]) {
  380. $data_tmp[date($dateformat, $intval[0])]++;
  381. $appendix[$tmpdata['id']] = 1;
  382. }
  383. }
  384. }
  385. $interval = 1;
  386. break;
  387. case 'closed': // Interval; all tickets closed between start and end,
  388.    // grouped by interval
  389. $stat['variable2'] = $stat['variable1'];
  390. $result = $db->query_return_array("SELECT ticket.id, ticket.$stat[variable1], ticket.date_closed
  391. FROM ticket
  392. WHERE $where
  393. AND date_closed >= '$startdate'
  394. AND date_closed < '$enddate'");
  395. if (is_array($intervals)) {
  396. foreach ($intervals as $intkey => $intval) {
  397. $data_tmp[date($dateformat, $intval[0])] = 0;
  398. if (is_array($result)) {
  399. foreach ($result AS $tmpdata) {
  400. if ($tmpdata['date_closed'] >= $intval[0] AND $tmpdata['date_closed'] < $intval[1]) {
  401. $data_tmp[date($dateformat, $intval[0])]++;
  402. $appendix[$tmpdata['id']] = 1;
  403. }
  404. }
  405. }
  406. }
  407. }
  408. $interval = 1;
  409. break;
  410. case 'reply': // Interval; all tickets replied to between start and end,
  411.   // grouped by interval
  412. $stat['variable2'] = $stat['variable1'];
  413. $result = $db->query_return_array("SELECT ticket.id, ticket.$stat[variable1], ticket_message.date
  414. FROM ticket, ticket_message
  415. WHERE $where
  416. AND ticket_message.date >= '$startdate'
  417. AND ticket_message.date < '$enddate'
  418. AND ticket.id = ticket_message.ticketid");
  419. foreach ($intervals as $intkey => $intval) {
  420. $data_tmp[date($dateformat, $intval[0])] = 0;
  421. foreach ($result AS $tmpdata) {
  422. if ($tmpdata['date'] >= $intval[0] AND $tmpdata['date'] < $intval[1]) {
  423. $data_tmp[date($dateformat, $intval[0])]++;
  424. $appendix[$tmpdata['id']] = 1;
  425. }
  426. }
  427. }
  428. $interval = 1;
  429. break;
  430. case '1streply_less': // Length; all tickets whose first response took less
  431.   // than the specified amount of time.
  432. $db->query("SELECT COUNT(*) AS totals, ticket.$stat[variable1], ticket_message.date 
  433. FROM ticket, ticket_message
  434. WHERE ticket.id = ticket_message.ticketid
  435. AND ((ticket.date_opened - ticket_message.date) < $duration) 
  436. GROUP BY $stat[variable1]");
  437. if ($result = $db->num_rows()) {
  438. $data_tmp[$stat['variable1']] = $result['totals'];
  439. }
  440. $db->query("SELECT ticket.id, ticket_message.date
  441. FROM ticket, ticket_message
  442. WHERE ((ticket.date_opened - ticket_message.date) < $duration)");
  443. while ($result = $db->row_array()) {
  444. $appendix[$result['id']] = 1;
  445. }
  446. $length = 1;
  447. break;
  448. case '1streply_greater': // Length; all tickets whose first response took more
  449.  // than the specified amount of time.
  450. $db->query("SELECT COUNT(*) AS totals, ticket.$stat[variable1], ticket_message.date 
  451. FROM ticket, ticket_message
  452. WHERE ticket.id = ticket_message.ticketid
  453. AND ((ticket.date_opened - ticket_message.date) >= $duration) 
  454. GROUP BY $stat[variable1]");
  455. if ($result = $db->num_rows()) {
  456. $data_tmp[$stat['variable1']] = $result['totals'];
  457. }
  458. $db->query("SELECT ticket.id, ticket_message.date
  459. FROM ticket, ticket_message
  460. WHERE ((ticket.date_opened - ticket_message.date) >= $duration)");
  461. while ($result = $db->row_array()) {
  462. $appendix[$result['id']] = 1;
  463. }
  464. $length = 1;
  465. break;
  466. case 'span_less': // Length; all tickets whose open-to-close duration is less
  467.   // than the specified amount of time.
  468. $db->query("SELECT COUNT(ticket.id) AS totals, ticket.$stat[variable1] 
  469. FROM ticket, ticket_def
  470. WHERE ((ticket.date_closed - ticket.date_opened) < $duration) 
  471. OR ((now() - ticket.date_opened) < $duration)
  472. GROUP BY $stat[variable1]");
  473. if ($result = $db->num_rows()) {
  474. while ($result = $db->row_array()) {
  475. $data_tmp[$result[$stat['variable1']]] = $result['totals'];
  476. }
  477. }
  478. $db->query("SELECT ticket.id
  479. FROM ticket
  480. WHERE ((ticket.date_closed - ticket.date_opened) < $duration)
  481. OR ((now() - ticket.date_opened) < $duration)");
  482. while ($result = $db->row_array()) {
  483. $appendix[$result['id']] = 1;
  484. }
  485. $length = 1;
  486. break;
  487. case 'span_more': // Length; all tickets whose open-to-close duration is more
  488.   // than the specified amount of time.
  489. $db->query("SELECT COUNT(ticket.id) AS totals, ticket.$stat[variable1] 
  490. FROM ticket
  491. WHERE ((ticket.date_closed - ticket.date_opened) >= $duration) 
  492. OR ((now() - ticket.date_opened) >= $duration)
  493. GROUP BY $stat[variable1]");
  494. if ($result = $db->num_rows()) {
  495. while ($result = $db->row_array()) {
  496. $data_tmp[$result[$stat['variable1']]] = $result['totals'];
  497. }
  498. }
  499. $db->query("SELECT ticket.id
  500. FROM ticket
  501. WHERE ((ticket.date_closed - ticket.date_opened) >= $duration)
  502. OR ((now() - ticket.date_opened) >= $duration)");
  503. while ($result = $db->row_array()) {
  504. $appendix[$result['id']] = 1;
  505. }
  506. $length = 1;
  507. break;
  508. }
  509. // If there *isn't* an interval, name translations haven't already been
  510. // done for us, so do them here.
  511. if (!$interval) {
  512. if (isset($data_tmp)) {
  513. if (is_array($data_tmp)) {
  514. if (stristr($stat['variable1'], 'custom')) { // If it's a custom field, we have to figure out names ourselves.
  515. foreach ($data_tmp AS $key => $val) { 
  516. $items = explode('|||', $key);
  517. $tmptitle = array();
  518. foreach ($items AS $ikey => $ival) {
  519. if (is_array($tfields[$stat['variable1']])) {
  520. foreach ($tfields[$stat['variable1']] AS $tkey => $tval) {
  521. if ($tval[0] == $ival) {
  522. $tmptitle[] = $tval[2];
  523. }
  524. }
  525. } else {
  526. $tmptitle[] = $tfields_names[$stat['variable1']];
  527. }
  528. }
  529. $change[$stat['variable1']][$key] = join(', ', $tmptitle);
  530. }
  531. }
  532. $data = change_index($data_tmp, $change[$stat['variable1']]);
  533. }
  534. }
  535. } else {
  536. if (isset($data_tmp)) {
  537. $data = $data_tmp;
  538. }
  539. }
  540. ################# 2ND VARIABLE PROCESSING #################
  541. // The 2nd variable is processed after the first has been run; each
  542. // "column" of the 1st variable's series of data is used as a key for the
  543. // 2nd variable run.
  544. if ($interval) { // If we're doing an interval search, use the dates as X series data
  545. switch ($stat['dateaffect']) {
  546. case 'opened': // By date-opened
  547. $result = $db->query_return_array("SELECT ticket.id, ticket.$stat[variable2], ticket.date_opened
  548. FROM ticket
  549. WHERE $where
  550. AND date_opened >= '$startdate'
  551. AND date_opened < '$enddate'");
  552. unset($tmp);
  553. foreach ($intervals as $intkey => $intval) {
  554. $tmp[$tmpdata[$stat['variable2']]] = 0;
  555. foreach ($result AS $tmpdata) {
  556. if ($tmpdata['date_opened'] >= $intval[0] AND $tmpdata['date_opened'] < $intval[1]) {
  557. $tmp[$tmpdata[$stat['variable2']]]++;
  558. }
  559. }
  560. if (isset($tmp)) {
  561. $data2[date($dateformat, $intval[0])] = change_index($tmp, $change[$stat['variable2']]);
  562. } else {
  563. $data2[date($dateformat, $intval[0])] = NULL;
  564. }
  565. }
  566. break;
  567. case 'closed': // By date-closed
  568. $result = $db->query_return_array("SELECT ticket.id, ticket.$stat[variable2], ticket.date_closed
  569. FROM ticket
  570. WHERE $where
  571. AND date_closed >= '$startdate'
  572. AND date_closed < '$enddate'");
  573. unset($tmp);
  574. foreach ($intervals as $intkey => $intval) {
  575. $tmp[$tmpdata[$stat['variable2']]] = 0;
  576. foreach ($result AS $tmpdata) {
  577. if ($tmpdata['date_closed'] >= $intval[0] AND $tmpdata['date_closed'] < $intval[1]) {
  578. $tmp[$tmpdata[$stat['variable2']]]++;
  579. }
  580. }
  581. if (isset($tmp)) {
  582. $data2[date($dateformat, $intval[0])] = change_index($tmp, $change[$stat['variable2']]);
  583. } else {
  584. $data2[date($dateformat, $intval[0])] = NULL;
  585. }
  586. }
  587. break;
  588. case 'reply': // By date of reply.
  589. $result = $db->query_return_array("SELECT ticket.id, ticket.$stat[variable2], ticket_message.date
  590. FROM ticket, ticket_message
  591. WHERE $where
  592. AND ticket_message.date >= '$startdate'
  593. AND ticket_message.date < '$enddate'
  594. AND ticket.id = ticket_message.ticketid");
  595. unset($tmp);
  596. foreach ($intervals as $intkey => $intval) {
  597. $tmp[$tmpdata[$stat['variable2']]] = 0;
  598. foreach ($result AS $tmpdata) {
  599. if ($tmpdata['date'] >= $intval[0] AND $tmpdata['date'] < $intval[1]) {
  600. $tmp[$tmpdata[$stat['variable2']]]++;
  601. }
  602. }
  603. if (isset($tmp)) {
  604. $data2[date($dateformat, $intval[0])] = change_index($tmp, $change[$stat['variable2']]);
  605. } else {
  606. $data2[date($dateformat, $intval[0])] = NULL;
  607. }
  608. }
  609. break;
  610. }
  611. } else {
  612. // Otherwise, process normally, once for each piece of data in the 1st
  613. // variable series.
  614. if (($stat['variable2'] AND isset($data_tmp)) and
  615. ($stat['variable1'] != $stat['variable2'])) {
  616. foreach ($data_tmp AS $key => $var) {
  617. $db->query("
  618. SELECT COUNT(*) AS totals, $stat[variable2]
  619. FROM ticket
  620. WHERE $where
  621. AND $stat[variable1] = '" . mysql_escape_string($key) . "'
  622. GROUP BY $stat[variable2]
  623. ");
  624. unset($tmp);
  625. if ($db->num_rows()) {
  626. while ($result = $db->row_array()) {
  627. $tmp[$result[$stat['variable2']]] = $result['totals'];
  628. }
  629. }
  630. if (stristr($stat['variable2'], 'custom')) { // If it's a custom field, we have to figure out names ourselves.
  631. foreach ($data_tmp AS $key => $val) { 
  632. $items = explode('|||', $key);
  633. $tmptitle = array();
  634. foreach ($items AS $ikey => $ival) {
  635. if (is_array($tfields[$stat['variable2']])) {
  636. foreach ($tfields[$stat['variable2']] AS $tkey => $tval) {
  637. if ($tval[0] == $ival) {
  638. $tmptitle[] = $tval[2];
  639. }
  640. }
  641. } else {
  642. $tmptitle[] = $tfields_names[$stat['variable2']];
  643. }
  644. }
  645. $change[$stat['variable2']][$key] = join(', ', $tmptitle);
  646. }
  647. }
  648. // Perform the index exchange so the table/graph rendering
  649. // functions can label each axis.
  650. $data_tmp2[$key] = change_index($tmp, $change[$stat['variable2']]);
  651. $db->query("
  652. SELECT id
  653. FROM ticket
  654. WHERE $where
  655. AND $stat[variable1] = '" . mysql_escape_string($key) . "'
  656. ");
  657. while ($result = $db->row_array()) {
  658. $appendix[$result['id']] = 1;
  659. }
  660. }
  661. $data2 = change_index($data_tmp2, $change[$stat['variable1']]);
  662. }
  663. }
  664. ################# GENERATE REPORT ENTRY #################
  665. // With the data array loaded, the statistic's output can be generated.
  666. if (isset($appendix)) { // Get the appendix ticket numbers into a list.
  667. $appendix = array_keys($appendix);
  668. }
  669. if (!isset($data)) {
  670. $data = NULL;
  671. }
  672. if (!isset($data2)) {
  673. $data2 = NULL;
  674. }
  675. // Now call the correct function to generate the requested output.
  676. switch($report['format']) {
  677. case 'default':
  678. case 'HTML': // HTML generation
  679. switch($stat['displaytype']) {
  680. case 'default':
  681. case 'data':
  682. echo make_table($data, $data2, $stat, $style);
  683. echo "<BR />n";
  684. break;
  685. case 'bar':
  686. echo make_barchart($data, $data2, $stat, $style);
  687. break;
  688. case 'databar':
  689. echo make_table($data, $data2, $stat, $style);
  690. echo make_barchart($data, $data2, $stat, $style);
  691. break;
  692. case 'pie':
  693. echo make_piechart($data, $data2, $stat, $style);
  694. break;
  695. case 'datapie':
  696. echo make_table($data, $data2, $stat, $style);
  697. echo make_piechart($data, $data2, $stat, $style);
  698. break;
  699. case 'csv': // If the user wants a CSV, we generate it and exit.
  700. header('Content-type: text/comma-separated-values');
  701. echo make_csv($data, $data2, $stat, $style);
  702. exit;
  703. }
  704. // Generate the appendix if it's been requested.
  705. if (($data OR $data2) AND isset($stat['appendix'])) {
  706. if (isset($appendix)) {
  707. echo make_appendix($appendix, $stat['displayfields'], $change);
  708. }
  709. }
  710. echo "<HR>n";
  711. break;
  712. case 'PDF': // PDF generation
  713. switch($stat['displaytype']) {
  714. case 'default':
  715. case 'data':
  716. make_pdf_table($data, $data2, $stat);
  717. break;
  718. case 'bar':
  719. make_barchart($data, $data2, $stat, $style, 1);
  720. break;
  721. case 'databar':
  722. make_pdf_table($data, $data2, $stat);
  723. make_barchart($data, $data2, $stat, $style, 1);
  724. break;
  725. case 'pie':
  726. make_piechart($data, $data2, $stat, $style, 1);
  727. break;
  728. case 'datapie':
  729. make_pdf_table($data, $data2, $stat);
  730. make_piechart($data, $data2, $stat, $style, 1);
  731. break;
  732. case 'csv':
  733. mistake('CSV type not supported in PDFs!');
  734. exit;
  735. break;
  736. }
  737. // Generate the appendix if it's been requested.
  738. if (($data OR $data2) AND $stat['appendix']) {
  739. echo make_pdf_appendix($appendix, $stat['displayfields'], $change);
  740. }
  741. break;
  742. }
  743. }
  744. // Now that all the statistics have been generated, if we have to mail or save
  745. // the report somewhere, gather it up and prepare it.
  746. if (isset($buffer)) { // We're saving or mailing this, not emitting it.
  747. if ($report['format'] == 'pdf') { // The attachment is a PDF
  748. $tmpfname = tempnam ("/tmp", "dp2_pdf");
  749. $pdf->Output($tmpfname);
  750. $handle = fopen($tmpfname, "rb");
  751. $output['data'] = fread($handle, filesize($tmpfname));
  752. fclose($handle);
  753. unlink($tmpfname);
  754. $output['name'] = 'report.pdf';
  755. $output['extension'] = 'pdf';
  756. } else { // The attachment is HTML
  757. $output['data'] = ob_get_contents();
  758. $output['name'] = 'report.html';
  759. $output['extension'] = 'html';
  760. }
  761. if ($report['email']) { // Send e-mail
  762. global $footer;
  763. eval(makeemaileval('message', 'TECHBODY_report', $subject));
  764. dp_mail($report['email'], $subject, $message, '', '', array($output));
  765. $msg = 'Report mailed to ' . htmlspecialchars_uni($report['email']) . '. ';
  766. }
  767. if (isset($handle)) { // Save to disk
  768. fwrite($handle, $output['data']);
  769. fclose($handle);
  770. if (isset($msg)) {
  771. $msg .= "Report saved in file $filename.";
  772. } else {
  773. $msg = "Report saved in file $filename.";
  774. }
  775. }
  776. ob_end_clean();
  777. if (!defined('CRONZONE')) { 
  778. // If this is *NOT* being run as a cron job, generate a header and let
  779. // the admin know the run is complete.
  780. admin_header('Reports', 'Save/E-Mail Report');
  781. print $msg;
  782. }
  783. exit;
  784. } else {
  785. // Finally, if the report is just meant to be viewed immediately, show it
  786. // and exit.
  787. if ($report['format'] == 'PDF') {
  788. $pdf->Output();
  789. }
  790. }
  791. // All done!
  792. // ============================================
  793. // Function make_appendix
  794. // ============================================
  795. // Generates an HTML table containing a list of
  796. // all tickets matched during the reporting run
  797. // ============================================
  798. // Arguments:
  799. //  appendix List of ticket numbers in
  800. // the appendix
  801. // appendix_fields
  802. // List of fields to show in
  803. // the appendix
  804. // change Column or row name mappings
  805. // to use.
  806. // ============================================
  807. // Returns:
  808. // The HTML to display.
  809. // ============================================
  810. function make_appendix($appendix, $appendix_fields = NULL, $change = array()) {
  811. global $db, $tfields, $tfields_names;
  812. $html = "<table cellpadding="0" cellspacing="0" width="100%"><tr><td bgcolor="#000000"><table cellspacing="1" cellpadding="3" width="100%">";
  813. $fields = explode(',', $appendix_fields);
  814. $total = count($fields);
  815. $count = 0;
  816. // If there weren't any tickets matched, or if the admin hasn't specified
  817. // anything to display, don't bother doing anything.
  818. if ($appendix_fields AND count($appendix)) {
  819. $appendix = array2sql($appendix);
  820. // Remap "user" fields to "userid" to match the ticket table.
  821. foreach ($fields as $key => $var) {
  822. if ($var == 'user') {
  823. $fields[$key] = 'userid';
  824. }
  825. }
  826. $appendix_fields = join(',', $fields);
  827. // Grab ticket data
  828. $db->query(
  829. "SELECT id, $appendix_fields
  830. FROM ticket
  831. WHERE id IN $appendix
  832. ");
  833. // If we don't get any rows, don't bother running through any results.
  834. // Otherwise, generate the table headers and run through the result set
  835. // showing each entry.
  836. if ($db->num_rows()) {
  837. $count++;
  838. $total++;
  839. $html .= "<tr bgcolor="#FFFF99"><td align="center" colspan="$total"><B>Found " . $db->num_rows() . " Ticket(s)</B></td></tr>";
  840. $html .= "<tr>n";
  841. // Generate the headings.
  842. foreach($fields as $key => $val) {
  843. switch ($val) {
  844. case 'subject': $label = 'Subject'; break;
  845. case 'priority' : $label = 'Priority'; break;
  846. case 'tech' : $label = 'Technician'; break;
  847. case 'is_open' : $label = 'Open/Closed'; break;
  848. case 'awaiting_tech' : $label = 'Awaiting...'; break;
  849. case 'category' : $label = 'Category'; break;
  850. case 'userid' : $label = 'User'; break;
  851. case 'id' : next; break;
  852. default: $label = $val;
  853. }
  854. if (stristr($val, 'custom')) {
  855. $label = $tfields_names[$val];
  856. }
  857. $html .= "<td bgcolor="#FFFFCC"><B>$label</B></TD>";
  858. }
  859. $html .= "<td align="center" bgcolor="#FFFFCC"><B>View</B></TD>";
  860. $html .= "</tr>n";
  861. // Run through each result and generate the row.
  862. while ($result = $db->row_array()) {
  863. $html .= "<tr>n";
  864. // Figure out what kind of field it is, and "massage" the
  865. // displayed data accordingly.
  866. foreach($fields as $key => $val) {
  867. switch ($val) {
  868. case 'id':
  869. next;
  870. break;
  871. case 'subject':
  872. if ($result[$val]) {
  873. $result[$val] = $result[$val];
  874. } else {
  875. $result[$val] = "<I>None</I>";
  876. }
  877. break;
  878. case 'category': $result[$val] = $change['category'][$result[$val]]; break;
  879. case 'priority': $result[$val] = $change['priority'][$result[$val]]; break;
  880. case 'tech': 
  881. if (isset($result[$val])) {
  882. $result[$val] = $change['tech'][$result[$val]];
  883. } else {
  884. $result[$val] = "<I>None Assigned</I>";
  885. }
  886. break;
  887. case 'userid': 
  888. if (isset($result[$val])) {
  889. $result[$val] = $change['userid'][$result[$val]];
  890. } else {
  891. $result[$val] = "<I>None</I>";
  892. }
  893. break;
  894. case 'is_open': $result[$val] = $change['is_open'][$result[$val]]; break;
  895. case 'awaiting_tech': $result[$val] = $change['awaiting_tech'][$result[$val]]; break;
  896. }
  897. // Again, for custom fields we have to calculate the
  898. // display name here.
  899. if (stristr($val, 'custom')) {
  900. $items = explode('|||', $result[$val]);
  901. $tmptitle = array();
  902. foreach ($items AS $ikey => $ival) {
  903. if (is_array($tfields[$val])) {
  904. foreach ($tfields[$val] AS $tkey => $tval) {
  905. if ($tval[0] == $ival) {
  906. $tmptitle[] = $tval[2];
  907. }
  908. }
  909. }
  910. }
  911. $result[$val] = join(', ', $tmptitle);
  912. }
  913. $html .= "<td bgcolor="#FFFFFF"><B>$result[$val]</B></TD>";
  914. }
  915. $html .= "<td align="center" bgcolor="#FFFFFF"><B><A HREF="../tech/tickets/ticketview.php?id=$result[id]">View</A></B></td>";
  916. $html .= "</tr>n";
  917. }
  918. } else {
  919. $html .= "<tr bgcolor="#FFFF99"><td align="center"><B>No Tickets Found</B></td></tr></table></td></tr></table>";
  920. }
  921. $html .= "</table></td></tr></table>";
  922. } else {
  923. $html = NULL;
  924. }
  925. return $html;
  926. }
  927. // ============================================
  928. // Function make_barchart
  929. // ============================================
  930. // Generates a graphical bar chart showing the
  931. // number of tickets matched per grouping
  932. // during the reporting run.
  933. // ============================================
  934. // Arguments:
  935. //  data First series of data
  936. // data2 First and second series of data
  937. // stat Statistic definition from DB
  938. // style Style definition from DB
  939. // do_pdf If true, add the resulting
  940. // graph to the PDF object,
  941. // otherwise, generate the file
  942. // and output a link to it.
  943. // ============================================
  944. // Returns:
  945. // Nothing; directly outputs to browser or
  946. // adds data to the PDF object
  947. // ============================================
  948. function make_barchart($data = NULL, $data2 = NULL, $stat, $style = NULL, $do_pdf = 0) {
  949. global $pdf;
  950. $html = "<font size="+1"><B>$stat[title]</B></font><br />n<b>$stat[description]</b></font><br />n<br />n";
  951. $html .= "<table cellpadding="0" cellspacing="0"><tr><td bgcolor="#000000"><table cellspacing="1" cellpadding="3">";
  952. include_once("./../includes/graph/jpgraph.php");
  953. include_once ("./../includes/graph/jpgraph_bar.php");
  954. error_reporting(0);
  955. if (!($data) AND !($data2)) { // If no tickets, don't bother with a graphic
  956. if ($do_pdf) {
  957. return make_pdf_table($data, $data2, $stat);
  958. } else {
  959. return make_table($data, $data2, $stat);
  960. }
  961. }
  962. // If we're making a PDF, set up the headers for the stat in the PDF
  963. // object.
  964. if ($do_pdf) {
  965. $pdf->SetFont('Times', 'B', 20);
  966. $pdf->Write(8, trim($stat['title'])."n");
  967. $pdf->SetFontSize(12);
  968. $pdf->Write(6, trim($stat['description'])."n");
  969. }
  970. // Set up the graph object and set basic attributes.
  971. $angle = 0;
  972. $graph = new Graph(700,350);
  973. $graph->SetScale('textlin');
  974. $graph->img->SetMargin(45,50,40,65);
  975. $graph->xaxis->SetFont(FF_FONT1,FS_BOLD);
  976. if ($data2) { // We're generating a group bar graph
  977. // Set up colors, and count rows and columns.
  978. $colors = array('gray', 'red', 'orange', 'yellow', 'green', 'blue', 'purple', 'white', 'black');
  979. foreach ($data2 AS $key1 => $var1) {
  980. $cols[$key1] = 1;
  981. }
  982. $rowdata = 0;
  983. foreach ($data2 AS $key1 => $var1) {
  984. if (is_array($var1)) {
  985. foreach ($var1 AS $key2 => $var2) {
  986. $rows[$key2] = 1;
  987. $rowdata = 1;
  988. }
  989. }
  990. }
  991. // If there isn't at least one row with valid data, don't continue with
  992. // the graphic.
  993. if (!$rowdata) {
  994. if ($do_pdf) {
  995. return make_pdf_table($data, $data2, $stat);
  996. } else {
  997. return make_table($data, $data2, $stat);
  998. }
  999. }
  1000. // Otherwise, generate the graph, one X axis entry at a time.
  1001. foreach ($rows AS $key1 => $var1) {
  1002. $axis_x = array();
  1003. $names = array();
  1004. foreach ($cols AS $key2 => $var2) {
  1005. $names[] = $key2;
  1006. if (strlen($key2) > 10) { 
  1007. $angle = 12; 
  1008. }
  1009. if (isset($data2[$key2][$key1])) {
  1010. $axis_x[] = $data2[$key2][$key1];
  1011. } else {
  1012. $axis_x[] = 0;
  1013. }
  1014. }
  1015. $bargraph = new BarPlot($axis_x);
  1016. if (!($color = next($colors))) {
  1017. $color = reset($colors);
  1018. }
  1019. $bargraph->SetFillColor($color);
  1020. if ($key1) {
  1021. $bargraph->SetLegend($key1);
  1022. } else {
  1023. $bargraph->SetLegend('None Assigned');
  1024. }
  1025. $bargraphs[] = $bargraph;
  1026. }
  1027. $group = new GroupBarPlot($bargraphs);
  1028. $graph->Add($group);
  1029. } elseif ($data) {
  1030. // Generate a single-axis graph.
  1031. foreach ($data AS $key1 => $var1) {
  1032. $names[] = $key1;
  1033. if (strlen($key1) > 10) { 
  1034. $angle = 12; 
  1035. }
  1036. if ($data[$key1]) {
  1037. $axis_x[] = $data[$key1];
  1038. } else {
  1039. $axis_x[] = 0;
  1040. }
  1041. }
  1042. $bargraph = new BarPlot($axis_x);
  1043. $graph->Add($bargraph);
  1044. }
  1045. // Labels tend to run together if there's more than just a few of them;
  1046. // when there's more than five, angle the labels to 25 degrees so they
  1047. // won't run into each other unless there's hundreds of them (that's where
  1048. // a table is most appropriate anyway)
  1049. if (count($names) > 5) {
  1050. $angle = 25;
  1051. }
  1052. // If there are more than 10 labels, we're probably running an
  1053. // interval-based statistic with hundreds of labels, so we don't need to
  1054. // show them all. Only show an eighth of the labels.
  1055. if (count($names) > 10) {
  1056. $count = 0;
  1057. $newnames = array();
  1058. $factor = (int)(count($names) / 8);
  1059. foreach ($names AS $namekey => $nameval) {
  1060. $count++;
  1061. if ($count == $factor) {
  1062. $newnames[] = $nameval;
  1063. $count = 0;
  1064. } else {
  1065. $newnames[] = '';
  1066. }
  1067. }
  1068. $names = $newnames;
  1069. }
  1070. // Now set up the graph axis labels and other features.
  1071. $graph->xaxis->SetTickLabels($names);
  1072. $graph->xaxis->SetLabelAngle($angle);
  1073. $graph->title->Set($stat['title']);
  1074. $graph->SetMarginColor("silver");
  1075. $graph->legend->Pos(0.01,0.01);
  1076. // Set up the title for the graph
  1077. $graph->title->SetFont(FF_VERDANA,FS_NORMAL,14);
  1078. $graph->xaxis->SetFont(FF_VERDANA,FS_NORMAL,8);
  1079. $graph->yaxis->SetFont(FF_VERDANA,FS_NORMAL,8);
  1080. $graph->legend->SetFont(FF_VERDANA,FS_NORMAL,8);
  1081. // Show 0 label on Y-axis (default is not to show)
  1082. $graph->yscale->ticks->SupressZeroLabel(false);
  1083. // Finally write the image to the browser.
  1084. $x = rand(1, 1000000);
  1085. $graph->Stroke("graphs/$x.png");
  1086. // If we're generating a PDF, write the image straight to the PDF object.
  1087. if ($do_pdf) {
  1088. $ix = $pdf->GetX();
  1089. $iy = $pdf->GetY();
  1090. $pdf->Image("graphs/$x.png", $ix, $iy, 150);
  1091. $pdf->AddPage();
  1092. return;
  1093. }
  1094. if (isset($stat['title_colour'])) {
  1095. $tc = " color="$stat[title_colour]"";
  1096. } else {
  1097. $tc = NULL;
  1098. }
  1099. if (isset($stat['description_colour'])) {
  1100. $dc = " color="$stat[description_colour]"";
  1101. } else {
  1102. $dc = NULL;
  1103. }
  1104. // Write the image tag to the browser.
  1105. echo "<img src="graphs/$x.png"><br />n";
  1106. echo "<I><FONT SIZE="-1" $dc>$stat[description]</FONT></I>n";
  1107. }
  1108. // ============================================
  1109. // Function make_piechart
  1110. // ============================================
  1111. // Generates a graphical pie chart showing the
  1112. // number of tickets matched per grouping
  1113. // during the reporting run.
  1114. // ============================================
  1115. // Arguments:
  1116. //  data First series of data
  1117. // data2 First and second series of data
  1118. // stat Statistic definition from DB
  1119. // style Style definition from DB
  1120. // do_pdf If true, add the resulting
  1121. // graph to the PDF object,
  1122. // otherwise, generate the file
  1123. // and output a link to it.
  1124. // ============================================
  1125. // Returns:
  1126. // Nothing; directly outputs to browser or
  1127. // adds data to the PDF object
  1128. // ============================================
  1129. // Note:
  1130. // For two-series graphs, we generate one
  1131. // pie graph per first-series datum since
  1132. // pie graphs are by nature one-axis-only.
  1133. function make_piechart($data = NULL, $data2 = NULL, $stat, $style = NULL, $do_pdf = 0) {
  1134. global $pdf;
  1135. $html = "<font size="+1"><B>$stat[title]</B></font><br />n<b>$stat[description]</b></font><br />n<br />n";
  1136. include_once("./../includes/graph/jpgraph.php");
  1137. include_once ("./../includes/graph/jpgraph_pie.php");
  1138. include_once ("./../includes/graph/jpgraph_pie3d.php");
  1139. error_reporting(0);
  1140. if (!($data) AND !($data2)) { // If no tickets, don't bother with a graphic
  1141. if ($do_pdf) {
  1142. return make_pdf_table($data, $data2, $stat);
  1143. } else {
  1144. return make_table($data, $data2, $stat);
  1145. }
  1146. }
  1147. $angle = 0;
  1148. if (!$do_pdf) {
  1149. echo $html;
  1150. }
  1151. // Use different sizes for PDF versus online viewing; PDF is higher
  1152. // resolution (when printed, anyway).
  1153. if ($do_pdf) {
  1154. $width = 800;
  1155. $height = 550;
  1156. $minifont = 8;
  1157. } else {
  1158. $width = 450;
  1159. $height = 300;
  1160. $minifont = 6;
  1161. }
  1162. if ($data2) { // Processing for 2-axis graphs
  1163. foreach ($data2 AS $key2 => $var2) {
  1164. $names = array();
  1165. $graph = new PieGraph($width,$height);
  1166. $graph->legend->SetFont(FF_VERDANA,FS_NORMAL,7);
  1167. $axis_x = array();
  1168. foreach ($var2 AS $key1 => $var1) {
  1169. if (!$key1) {
  1170. if (!$data2[$key2][$key1]) {
  1171. // Don't even bother putting this on the legend if it won't show up
  1172. // on the pie graph.
  1173. continue;
  1174. }
  1175. }
  1176. if ($data2[$key2][$key1]) {
  1177. $axis_x[] = $data2[$key2][$key1];
  1178. } else {
  1179. $axis_x[] = 0;
  1180. }
  1181. if (!$key1) {
  1182. $key1 = '(none)';
  1183. }
  1184. $names[] = $key1;
  1185. }
  1186. if (!array_sum($axis_x)) {
  1187. if ($do_pdf) {
  1188. $pdf->Write(8, "No data to show for $key2.");
  1189. }
  1190. continue;
  1191. }
  1192. // Set up the graph's attributes and labels, then plot.
  1193. $piegraph = new PiePlot3D($axis_x);
  1194. $piegraph->title->SetFont(FF_VERDANA,FS_NORMAL,14);
  1195. $piegraph->value->SetFont(FF_VERDANA,FS_NORMAL,$minifont);
  1196. $piegraph->title->Set($key2);
  1197. $piegraph->SetLegends($names);
  1198. $piegraph->SetCenter(0.5,0.5);
  1199. $piegraph->SetSize(0.5);
  1200. $graph->Add($piegraph);
  1201. $graph->legend->Pos(0.01,0.01);
  1202. $graph->SetMarginColor("silver");
  1203. // Finally write the graph to disk.
  1204. $x = rand(1, 1000000);
  1205. $graph->Stroke("graphs/$x.png");
  1206. if ($do_pdf) { // Add to the PDF object.
  1207. $pdf->SetFont('Times', 'B', 20);
  1208. $pdf->Write(8, trim($stat['title'])."n");
  1209. $pdf->SetFontSize(12);
  1210. $pdf->Write(6, trim($stat['description'])."n");
  1211. $ix = $pdf->GetX();
  1212. $iy = $pdf->GetY();
  1213. $pdf->Image("graphs/$x.png", $ix, $iy, 150);
  1214. $pdf->AddPage();
  1215. continue;
  1216. }
  1217. if ($stat['title_colour']) {
  1218. $tc = " color="$stat[title_colour]"";
  1219. }
  1220. if ($stat['description_colour']) {
  1221. $dc = " color="$stat[description_colour]"";
  1222. }
  1223. // Emit an image tag to the browser.
  1224. echo "<img src="graphs/$x.png"><br /><br />n";
  1225. }
  1226. } elseif ($data) { // 1-axis data processing
  1227. if ($do_pdf) {
  1228. $pdf->SetFont('Times', 'B', 20);
  1229. $pdf->Write(8, trim($stat['title'])."n");
  1230. $pdf->SetFontSize(12);
  1231. $pdf->Write(6, trim($stat['description'])."n");
  1232. }
  1233. $graph = new PieGraph($width,$height);
  1234. foreach ($data AS $key1 => $var1) {
  1235. $names[] = $key1;
  1236. if ($data[$key1]) {
  1237. $axis_x[] = $data[$key1];
  1238. } else {
  1239. $axis_x[] = 0;
  1240. }
  1241. }
  1242. // Set up the graph's attributes and labels, then plot.
  1243. $piegraph = new PiePlot3D($axis_x);
  1244. $piegraph->title->SetFont(FF_VERDANA,FS_NORMAL,14);
  1245. $piegraph->value->SetFont(FF_VERDANA,FS_NORMAL,$minifont);
  1246. $piegraph->title->Set($stat['title']);
  1247. $graph->legend->SetFont(FF_VERDANA,FS_NORMAL,8);
  1248. $piegraph->SetLegends($names);
  1249. $piegraph->SetCenter(0.5,0.41);
  1250. $piegraph->SetSize(0.45);
  1251. $graph->Add($piegraph);
  1252. $graph->legend->Pos(0.01,0.01);
  1253. $graph->SetMarginColor("silver");
  1254. // Finally write the graph to disk.
  1255. $x = rand(1, 1000000);
  1256. $graph->Stroke("graphs/$x.png");
  1257. if ($do_pdf) { // Add to the PDF object.
  1258. $ix = $pdf->GetX();
  1259. $iy = $pdf->GetY();
  1260. $pdf->Image("graphs/$x.png", $ix, $iy, 150);
  1261. $pdf->AddPage();
  1262. return;
  1263. }
  1264. if (isset($stat['title_colour'])) {
  1265. $tc = " color="$stat[title_colour]"";
  1266. } else {
  1267. $tc = NULL;
  1268. }
  1269. if (isset($stat['description_colour'])) {
  1270. $dc = " color="$stat[description_colour]"";
  1271. } else {
  1272. $dc = NULL;
  1273. }
  1274. // Emit image tag to the browser.
  1275. echo "<img src="graphs/$x.png"><br />n";
  1276. echo "<I><FONT SIZE="-1" $dc>$stat[description]</FONT></I>n";
  1277. }
  1278. }
  1279. // ============================================
  1280. // Function make_table
  1281. // ============================================
  1282. // Generates an HTML table showing the
  1283. // number of tickets matched per grouping
  1284. // during the reporting run.
  1285. // ============================================
  1286. // Arguments:
  1287. //  data First series of data
  1288. // data2 First and second series of data
  1289. // stat Statistic definition from DB
  1290. // ============================================
  1291. // Returns:
  1292. // The HTML to display.
  1293. // ============================================
  1294. function make_table($data = NULL, $data2 = NULL, $stat) {
  1295. // Set up styles, if any.
  1296. if (isset($stat['title_colour'])) {
  1297. $tc = " color="$stat[title_colour]"";
  1298. } else {
  1299. $tc = NULL;
  1300. }
  1301. if (isset($stat['description_colour'])) {
  1302. $dc = " color="$stat[description_colour]"";
  1303. } else {
  1304. $dc = NULL;
  1305. }
  1306. // Generate the table headings.
  1307. $html = "<font size="+1" $tc><B>$stat[title]</B></font><br />n<font $dc><b>$stat[description]</b></font><br />n<br />n";
  1308. $html .= "<table cellpadding="0" cellspacing="0"><tr><td bgcolor="#000000"><table cellspacing="1" cellpadding="3">";
  1309. if ((!$data) AND (!$data2)) { // If it's an empty data set, return an empty table
  1310. $html .= "<td bgcolor="#FFFF99">No tickets found</td></tr></table></td></tr></table>";
  1311. return $html;
  1312. }
  1313. if ($data2) { // Two-axis processing
  1314. // Draw the heading row (X axis)
  1315. $html .= "<tr><td bgcolor="#FFFF99">&nbsp;</td>n";
  1316. foreach ($data2 AS $key1 => $var1) {
  1317. $html .= "<td bgcolor="#FFFFCC"><b>$key1</b></td>";
  1318. $cols[$key1] = 1;
  1319. }
  1320. $html .= "</tr>n";
  1321. $rowdata = 0;
  1322. // Now figure out what rows we've got
  1323. foreach ($data2 AS $key1 => $var1) {
  1324. if (is_array($var1)) {
  1325. foreach ($var1 AS $key2 => $var2) {
  1326. $rows[$key2] = 1;
  1327. $rowdata = 1;
  1328. }
  1329. }
  1330. }
  1331. // Now render the table
  1332. if (!$rowdata) { // Another way out, just in case there aren't any tickets matched in any row
  1333. $html = "<font size="+1" $tc><B>$stat[title]</B></font><br />n<font $dc><b>$stat[description]</b></font><br />n<br />n";
  1334. $html .= "<table cellpadding="0" cellspacing="0"><tr><td bgcolor="#000000"><table cellspacing="1" cellpadding="3">";
  1335. $html .= "<td bgcolor="#FFFF99">No tickets found</td></tr></table></td></tr></table>";
  1336. return $html;
  1337. }
  1338. // Generate each row of the table.
  1339. foreach ($rows AS $key1 => $val1) {
  1340. $html .= "<tr>";
  1341. if (!$key1) {
  1342. $key1 = "<I>None Assigned</I>";
  1343. }
  1344. $html .= "<td bgcolor="#FFFFCC" align="right"><B>$key1</B></td>n";
  1345. foreach ($cols AS $key2 => $val2) {
  1346. if ($data2[$key2][$key1]) {
  1347. $html .= "<td bgcolor="#FFFFFF">" . $data2[$key2][$key1] . "</td>n";
  1348. } else {
  1349. $html .= "<td bgcolor="#FFFFFF"><font color="gray">0</font></td>n";
  1350. }
  1351. $colnum++;
  1352. }
  1353. $html .= "</tr>";
  1354. }
  1355. $html .= "</table></td></tr></table>";
  1356. } else {
  1357. // 1-axis processing.
  1358. foreach ($data AS $key => $var) {
  1359. if (!$key) {
  1360. $key = "<I>None Assigned</I>";
  1361. }
  1362. $html .= "<tr><td bgcolor="#FFFFCC"><b>$key</b></td><td bgcolor="#FFFFFF">$var tickets</td></tr>";
  1363. }
  1364. $html .= "</table></td></tr></table>";
  1365. }
  1366. return $html;
  1367. }
  1368. // ============================================
  1369. // Function make_csv
  1370. // ============================================
  1371. // Generates a CSV file containing the
  1372. // number of tickets matched per grouping
  1373. // during the reporting run.
  1374. // ============================================
  1375. // Arguments:
  1376. //  data First series of data
  1377. // data2 First and second series of data
  1378. // stat Statistic definition from DB
  1379. // ============================================
  1380. // Returns:
  1381. // The CSV data.
  1382. // ============================================
  1383. function make_csv($data = NULL, $data2 = NULL, $stat) {
  1384. if ((!$data) AND (!$data2)) { // If it's an empty data set, return an empty table
  1385. return "No tickets found.";
  1386. }
  1387. if ($data2) {
  1388. $headers = array('"Labels"');
  1389. // Draw the heading row (X axis)
  1390. foreach ($data2 AS $key1 => $var1) {
  1391. $headers[] = ""$key1"";
  1392. $cols[$key1] = 1;
  1393. }
  1394. $ret = join(',',$headers) . "nn";
  1395. // Now figure out what rows we've got
  1396. foreach ($data2 AS $key1 => $var1) {
  1397. foreach ($var1 AS $key2 => $var2) {
  1398. $rows[$key2] = 1;
  1399. }
  1400. }
  1401. // Now produce the records
  1402. foreach ($rows AS $key1 => $val1) {
  1403. $row = array();
  1404. $row[] = ""$key1"";
  1405. foreach ($cols AS $key2 => $val2) {
  1406. if ($data2[$key2][$key1]) {
  1407. $row[] = '"' . $data2[$key2][$key1] . '"';
  1408. } else {
  1409. $row[] = '"0"';
  1410. }
  1411. $colnum++;
  1412. }
  1413. $ret .= join(',',$row) . "n";
  1414. }
  1415. } else {
  1416. $headers = array();
  1417. $row = array();
  1418. foreach ($data AS $key => $var) {
  1419. $headers[] = ""$key"";
  1420. $row[] = ""$var"";
  1421. }
  1422. $ret .= join(',',$headers);
  1423. $ret .= "nn";
  1424. $ret .= join(',',$row);
  1425. }
  1426. return $ret;
  1427. }
  1428. // ============================================
  1429. // Function make_pdf_table
  1430. // ============================================
  1431. // Generates a PDF table showing the
  1432. // number of tickets matched per grouping
  1433. // during the reporting run.
  1434. // ============================================
  1435. // Arguments:
  1436. //  data First series of data
  1437. // data2 First and second series of data
  1438. // stat Statistic definition from DB
  1439. // ============================================
  1440. // Returns:
  1441. // Nothing; directly adds to the PDF
  1442. // object.
  1443. // ============================================
  1444. function make_pdf_table($data = NULL, $data2 = NULL, $stat) {
  1445. global $pdf;
  1446. $pdf->SetFont('Times', 'B', 20);
  1447. $pdf->Write(8, trim($stat['title'])."n");
  1448. $pdf->SetFontSize(12);
  1449. $pdf->Write(6, trim($stat['description'])."n");
  1450. if ((!$data) AND (!$data2)) { // If it's an empty data set, return an empty table
  1451. $pdf->SetFont('Arial', 'B', 10);
  1452. $width = $pdf->GetStringWidth('No tickets found');
  1453. $pdf->SetFillColor(255, 255, 200);
  1454. $pdf->Cell($width+2, 5, 'No tickets found', 1, 1, 'C', 1);
  1455. return;
  1456. }
  1457. if ($data2) { // 2-axis processing.
  1458. $table = array();
  1459. // Load the heading row (X axis) array
  1460. $pdf->SetFont('Arial', 'B', 10);
  1461. $col[] = '';
  1462. $widths[] = $pdf->GetStringWidth('');
  1463. foreach ($data2 AS $key1 => $var1) {
  1464. $col[] = $key1;
  1465. $widths[] = $pdf->GetStringWidth($key1);
  1466. $table[0] = $col;
  1467. }
  1468. // Load the heading column (Y axis) helper array
  1469. foreach ($data2 AS $key1 => $var1) {
  1470. if (is_array($var1)) {
  1471. foreach ($var1 AS $key2 => $var2) {
  1472. $rows[$key2] = 1;
  1473. }
  1474. }
  1475. }
  1476. if (!is_array($rows)) { // Another way out, just in case there aren't any tickets matched in any row
  1477. $pdf->SetFont('Arial', 'B', 10);
  1478. $width = $pdf->GetStringWidth('No tickets found');
  1479. $pdf->SetFillColor(255, 255, 200);
  1480. $pdf->Cell($width+2, 5, 'No tickets found', 1, 1, 'C', 1);
  1481. return $html;
  1482. }
  1483. // Load the table array
  1484. foreach ($rows AS $key1 => $val1) {
  1485. $colnum = 0;
  1486. unset($row);
  1487. $width = $pdf->GetStringWidth($key1);
  1488. if ($width > $widths[0]) {
  1489. $widths[0] = $width;
  1490. }
  1491. $row[] = $key1;
  1492. array_shift($col);
  1493. foreach ($col AS $key2 => $val2) {
  1494. if ($data2[$val2][$key1]) {
  1495. $val = $data2[$val2][$key1];
  1496. } else {
  1497. $val = 'None';
  1498. }
  1499. if ($colnum) { // regular cell
  1500. $pdf->SetFont('Arial', '', 10);
  1501. } else { // heading column
  1502. $pdf->SetFont('Arial', 'B', 10);
  1503. }
  1504. $width = $pdf->GetStringWidth($val);
  1505. if ($width > $widths[$colnum]) {
  1506. $widths[$colnum] = $width;
  1507. }
  1508. $row[] = $val;
  1509. }
  1510. array_unshift($col, '');
  1511. $table[] = $row;
  1512. }
  1513. // Render the table
  1514. $maxwidth = 195;
  1515. if (array_sum($widths) > $maxwidth) { 
  1516. // If the total width of all columns is greater than the page's
  1517. // width, we have to truncate.
  1518. $maxcol = 0;
  1519. $totwid = 0;
  1520. foreach ($widths AS $key => $val) {
  1521. $totwid = $totwid + $val;
  1522. if ($totwid <= $maxwidth) {
  1523. $maxcol++;
  1524. } else {
  1525. $maxwidth = 0;
  1526. }
  1527. }
  1528. }
  1529. // Render the heading row
  1530. $headings = array_shift($table);
  1531. $pdf->SetFont('Arial', 'B', 10);
  1532. $pdf->SetFillColor(255,255,200);
  1533. $colnum = 0;
  1534. foreach ($headings as $key => $val) {
  1535. if ((($colnum+1) < $maxcol) OR (!$maxcol)) {
  1536. $pdf->Cell($widths[$colnum]+2, 4.5, $val, 1, 0, 'C', 1);
  1537. $colnum++;
  1538. }
  1539. }
  1540. $pdf->Ln();
  1541. // Render the remaining rows
  1542. $pdf->SetFillColor(220,220,220);
  1543. $fill = 0;
  1544. foreach ($table as $key => $val) {
  1545. $cell = array_shift($val);
  1546. $pdf->SetFont('Arial', 'B', 10);
  1547. $pdf->Cell($widths[0]+2, 4.5, $cell, 1, 0, 'R', $fill);
  1548. $pdf->SetFont('Arial', '', 10);
  1549. $colnum = 1;
  1550. foreach ($val as $key => $val2) {
  1551. if ((($colnum+1) < $maxcol) OR (!$maxcol)) {
  1552. if ($val2) {
  1553. $pdf->Cell($widths[$colnum]+2, 4.5, $val2, 1, 0, 'C', $fill);
  1554. } else {
  1555. $pdf->SetFont('Arial', 'I', 10);
  1556. $pdf->Cell($widths[$colnum]+2, 4.5, 'None', 1, 0, 'C', $fill);
  1557. $pdf->SetFont('Arial', '', 10);
  1558. }
  1559. $colnum++;
  1560. }
  1561. }
  1562. $pdf->Ln();
  1563. $fill=!$fill;
  1564. }
  1565. } else { // 1-axis processing.
  1566. // Calculate widths
  1567. $widths = array();
  1568. foreach ($data AS $key => $val) {
  1569. if (!$key) {
  1570. $key = "None";
  1571. }
  1572. $pdf->SetFont('Arial', 'B', 10);
  1573. $width = $pdf->GetStringWidth($key);
  1574. if ($width > $widths[0]) {
  1575. $widths[0] = $width;
  1576. }
  1577. $pdf->SetFont('Arial', '', 10);
  1578. $width = $pdf->GetStringWidth($val);
  1579. if ($width > $widths[1]) {
  1580. $widths[1] = $width;
  1581. }
  1582. }
  1583. // Render table
  1584. $pdf->SetFillColor(255,255,200);
  1585. foreach ($data AS $key => $val) {
  1586. $pdf->Cell($widths[0]+2, 4.5, $key, 1, 0, 'R', 1);
  1587. $pdf->Cell($widths[1]+2, 4.5, $val, 1, 1, 'L', 1);
  1588. }
  1589. }
  1590. $pdf->Ln();
  1591. return;
  1592. }
  1593. // ============================================
  1594. // Function make_pdf_appendix
  1595. // ============================================
  1596. // Generates a PDF table containing a list of
  1597. // all tickets matched during the reporting run
  1598. // ============================================
  1599. // Arguments:
  1600. //  appendix List of ticket numbers in
  1601. // the appendix
  1602. // appendix_fields
  1603. // List of fields to show in
  1604. // the appendix
  1605. // change Column or row name mappings
  1606. // to use.
  1607. // ============================================
  1608. // Returns:
  1609. // Nothing; adds to the PDF object
  1610. // ============================================
  1611. function make_pdf_appendix($appendix, $appendix_fields = NULL, $change = array()) {
  1612. global $db, $pdf, $tfields, $tfields_names;
  1613. $pdf->SetFont('Arial', 'B', 10);
  1614. $fields = explode(',', $appendix_fields);
  1615. $total = count($fields);
  1616. if ($appendix_fields AND count($appendix)) {
  1617. $appendix = array2sql($appendix);
  1618. foreach ($fields as $key => $var) {
  1619. if ($var == 'user') {
  1620. $fields[$key] = 'userid';
  1621. }
  1622. }
  1623. $appendix_fields = join(',', $fields);
  1624. $db->query(
  1625. "SELECT id, $appendix_fields
  1626. FROM ticket
  1627. WHERE id IN $appendix
  1628. ");
  1629. // Run through the result set.
  1630. if ($db->num_rows()) {
  1631. $colnum = 0;
  1632. $total++;
  1633. $tickets = $db->num_rows();
  1634. $row = array();
  1635. foreach($fields as $key => $val) {
  1636. switch ($val) {
  1637. case 'subject': $label = 'Subject'; break;
  1638. case 'priority' : $label = 'Priority'; break;
  1639. case 'tech' : $label = 'Technician'; break;
  1640. case 'is_open' : $label = 'Open/Closed'; break;
  1641. case 'awaiting_tech' : $label = 'Awaiting...'; break;
  1642. case 'userid' : $label = 'User'; break;
  1643. case 'category' : $label = 'Category'; break;
  1644. case 'id' : next; break;
  1645. default: $label = $val;
  1646. }
  1647. if (stristr($val, 'custom')) {
  1648. $label = $tfields_names[$val];
  1649. }
  1650. $row[] = $label;
  1651. $widths[$colnum] = $pdf->GetStringWidth($label);
  1652. $colnum++;
  1653. }
  1654. $table[] = $row;
  1655. $row = array();
  1656. $pdf->SetFont('Arial', '', 10);
  1657. while ($result = $db->row_array()) {
  1658. $row = array();
  1659. $colnum = 0;
  1660. foreach($fields as $key => $val) {
  1661. switch ($val) {
  1662. case 'id':
  1663. next;
  1664. break;
  1665. case 'subject':
  1666. if ($result[$val]) {
  1667. $result[$val] = $result[$val];
  1668. } else {
  1669. $result[$val] = "None";
  1670. }
  1671. break;
  1672. case 'category': $result[$val] = $change['category'][$result[$val]]; break;
  1673. case 'priority': $result[$val] = $change['priority'][$result[$val]]; break;
  1674. case 'tech': 
  1675. if ($result[$val]) {
  1676. $result[$val] = $change['tech'][$result[$val]];
  1677. } else {
  1678. $result[$val] = "None";
  1679. }
  1680. break;
  1681. case 'userid': 
  1682. if ($result[$val]) {
  1683. $result[$val] = $change['userid'][$result[$val]];
  1684. } else {
  1685. $result[$val] = "<I>None</I>";
  1686. }
  1687. break;
  1688. case 'is_open': $result[$val] = $change['is_open'][$result[$val]]; break;
  1689. case 'awaiting_tech': $result[$val] = $change['awaiting_tech'][$result[$val]]; break;
  1690. }
  1691. if (stristr($val, 'custom')) {
  1692. $items = explode('|||', $result[$val]);
  1693. $tmptitle = array();
  1694. foreach ($items AS $ikey => $ival) {
  1695. if (is_array($tfields[$val])) {
  1696. foreach ($tfields[$val] AS $tkey => $tval) {
  1697. if ($tval[0] == $ival) {
  1698. $tmptitle[] = $tval[2];
  1699. }
  1700. }
  1701. }
  1702. }
  1703. $result[$val] = join(', ', $tmptitle);
  1704. }
  1705. $row[] = $result[$val];
  1706. $width = $pdf->GetStringWidth($result[$val]);
  1707. if ($width > $widths[$colnum]) {
  1708. $widths[$colnum] = $width;
  1709. }
  1710. $colnum++;
  1711. }
  1712. $table[] = $row;
  1713. }
  1714. // "Letter" size paper is 215.9 mm wide, defaults to 10mm margins.
  1715. $maxwidth = 195;
  1716. if (array_sum($widths) > $maxwidth) { 
  1717. // If the total width of all columns is greater than the page's
  1718. // width, we have to truncate.
  1719. $maxcol = 0;
  1720. $totwid = 0;
  1721. foreach ($widths AS $key => $val) {
  1722. $totwid = $totwid + $val;
  1723. if ($totwid <= $maxwidth) {
  1724. $maxcol++;
  1725. } else {
  1726. $maxwidth = 0;
  1727. }
  1728. }
  1729. }
  1730. $pdf->SetFont('Arial', 'B', 10);
  1731. $colnum = 0;
  1732. $row = array_shift($table);
  1733. foreach ($row as $key => $val) {
  1734. if ((($colnum+1) < $maxcol) OR (!$maxcol)) {
  1735. $pdf->Cell($widths[$colnum]+2, 4.5, $val, 1, 0, 'C', 1);
  1736. }
  1737. $colnum++;
  1738. }
  1739. $pdf->Ln();
  1740. $pdf->SetFillColor(220,220,220);
  1741. $fill = 0; 
  1742. $pdf->SetFont('Arial', '', 10);
  1743. foreach ($table as $key => $val) {
  1744. $colnum = 0;
  1745. foreach ($val as $key2 => $val2) {
  1746. if ((($colnum+1) < $maxcol) OR (!$maxcol)) {
  1747. if ($val2) {
  1748. $pdf->Cell($widths[$colnum]+2, 4.5, $val2, 1, 0, 'C', $fill);
  1749. } else {
  1750. $pdf->SetFont('Arial', 'I', 10);
  1751. $pdf->Cell($widths[$colnum]+2, 4.5, 'None', 1, 0, 'C', $fill);
  1752. $pdf->SetFont('Arial', '', 10);
  1753. }
  1754. }
  1755. $colnum++;
  1756. }
  1757. $pdf->Ln();
  1758. $fill=!$fill;
  1759. }
  1760. $pdf->SetFont('Arial', 'I', 10);
  1761. $width = $pdf->GetStringWidth("$tickets tickets found");
  1762. $pdf->Cell($width, 5, "$tickets tickets found", 0, 1, 'L');
  1763. $pdf->Ln();
  1764. $pdf->Ln();
  1765. } else {
  1766. $pdf->SetFont('Arial', 'B', 10);
  1767. $width = $pdf->GetStringWidth('No tickets found');
  1768. $pdf->SetFillColor(255, 255, 200);
  1769. $pdf->Cell($width+2, 5, 'No tickets found', 1, 1, 'C', 1);
  1770. }
  1771. }
  1772. return;
  1773. }