+ Added Ticket Stats as a plugin
Jeremy D

Jeremy D commited on 2011-11-04 17:40:18
Showing 6 changed files, with 740 additions and 2 deletions.

... ...
@@ -0,0 +1,30 @@
1
+<?php
2
+###############################################################
3
+#         Simple Desk Project - www.simpledesk.net            #
4
+###############################################################
5
+#       An advanced help desk modifcation built on SMF        #
6
+###############################################################
7
+#                                                             #
8
+#         * Copyright 2010 - SimpleDesk.net                   #
9
+#                                                             #
10
+#   This file and its contents are subject to the license     #
11
+#   included with this distribution, license.txt, which       #
12
+#   states that this software is New BSD Licensed.            #
13
+#   Any questions, please contact SimpleDesk.net              #
14
+#                                                             #
15
+###############################################################
16
+# SimpleDesk Version: 2.0 Anatidae                            #
17
+# File Info: SDPluginFrontPage.english.php / 2.0 Anatidae     #
18
+###############################################################
19
+// Version: 2.0 Anatidae; SimpleDesk main language file
20
+
21
+// Important! Before editing these language files please read the text at the top of index.english.php.
22
+
23
+$txt['shdp_stats'] = 'Statistics';
24
+$txt['shdp_stats_desc'] = 'This plugin allows you to view statistics of your helpdesk.';
25
+
26
+$txt['shdp_enable_stats'] = 'Enable stats';
27
+
28
+
29
+$txt['shdp_stats_main'] ='j';
30
+$txt['shdp_stats_main_desc'] ='Stats';
... ...
@@ -0,0 +1,407 @@
1
+<?php
2
+###############################################################
3
+#         Simple Desk Project - www.simpledesk.net            #
4
+###############################################################
5
+#       An advanced help desk modifcation built on SMF        #
6
+###############################################################
7
+#                                                             #
8
+#         * Copyright 2010 - SimpleDesk.net                   #
9
+#                                                             #
10
+#   This file and its contents are subject to the license     #
11
+#   included with this distribution, license.txt, which       #
12
+#   states that this software is New BSD Licensed.            #
13
+#   Any questions, please contact SimpleDesk.net              #
14
+#                                                             #
15
+###############################################################
16
+# SimpleDesk Version: 2.0 Anatidae                            #
17
+# File Info: SDPluginFrontPage.php / 2.0 Anatidae             #
18
+###############################################################
19
+
20
+/**
21
+ *	This file handles the replacement front page.
22
+ *
23
+ *	@package plugin-stats
24
+ *	@since 2.0
25
+ */
26
+
27
+if (!defined('SMF'))
28
+	die('Hacking attempt...');
29
+
30
+/*
31
+ * Called from the SMF admin menu hook
32
+ */
33
+function shd_stats_adminmenu(&$admin_areas)
34
+{
35
+	global $context, $modSettings, $txt;
36
+
37
+	if (allowedTo('admin_forum') && !empty($modSettings['shdp_enable_stats']))
38
+		$admin_areas['helpdesk_info']['areas']['helpdesk_info']['subsections']['stats'] = array($txt['shdp_stats']);
39
+}
40
+
41
+function shd_stats_hdadmininfo(&$subactions)
42
+{
43
+	global $context, $modSettings, $txt;
44
+
45
+	if (!allowedTo('admin_forum') || empty($modSettings['shdp_enable_stats']))
46
+		return;
47
+
48
+	$subactions['stats'] = array(
49
+		'function' => 'shd_stats_source',
50
+		'icon' => '../reports.gif',
51
+		'title' => $txt['shdp_stats']
52
+	);
53
+
54
+	$context[$context['admin_menu_name']]['tab_data']['tabs']['stats'] = $subactions['stats'];
55
+}
56
+
57
+/*
58
+ * The options screen for the stats
59
+ */
60
+function shd_stats_admin($config_vars)
61
+{
62
+	$config_vars[] = '';
63
+	$config_vars[] = array('check', 'shdp_enable_stats');
64
+}
65
+
66
+/*
67
+ * The source file for the actual stats page.
68
+ */
69
+function shd_stats_source()
70
+{
71
+	global $modSettings, $context;
72
+
73
+	// All possible stat info.
74
+	$stats = array(
75
+		'status',
76
+		'today',
77
+		'most',
78
+		'average',
79
+		'totals',
80
+		'urgency',
81
+		'history',
82
+	);
83
+
84
+	// Loop out the stat info.
85
+	$context['shd_stats'] = array();
86
+	foreach ($stats as $function)
87
+	{
88
+		$func = 'shd_stats_' . $function;
89
+		$context['shd_stats'][$function] = $func();
90
+
91
+	}
92
+
93
+	loadTemplate('sd_plugins_template/SDPluginStats');
94
+}
95
+
96
+// This gets our ticket status info.
97
+function shd_stats_status()
98
+{
99
+	global $smcFunc;
100
+
101
+	$status = array(
102
+		TICKET_STATUS_NEW => 0,
103
+		TICKET_STATUS_PENDING_STAFF => 0,
104
+		TICKET_STATUS_PENDING_USER => 0,
105
+		TICKET_STATUS_CLOSED => 0,
106
+		TICKET_STATUS_WITH_SUPERVISOR => 0,
107
+		TICKET_STATUS_ESCALATED => 0,
108
+		TICKET_STATUS_DELETED => 0
109
+	);
110
+
111
+	// Collect up some numbers.
112
+	$request = $smcFunc['db_query']('', '
113
+		SELECT COUNT(id_ticket) AS count, status
114
+		FROM {db_prefix}helpdesk_tickets
115
+		WHERE status IN ({array_int:status})
116
+		GROUP BY status',
117
+		array(
118
+			'status' => array_keys($status)
119
+	));
120
+
121
+	while ($row = $smcFunc['db_fetch_assoc']($request))
122
+		$status[$row['status']] = $row['count'];
123
+	$smcFunc['db_free_result']($request);
124
+
125
+	$status['total_open'] = $status[TICKET_STATUS_NEW] + $status[TICKET_STATUS_PENDING_STAFF] + $status[TICKET_STATUS_PENDING_USER] + $status[TICKET_STATUS_WITH_SUPERVISOR] + $status[TICKET_STATUS_ESCALATED];
126
+	$status['total_closed'] = $status[TICKET_STATUS_CLOSED] + $status[TICKET_STATUS_DELETED];
127
+
128
+	$status['total_total'] = $status['total_open'] + $status['total_closed'];
129
+
130
+	// We are fast.
131
+	if ($status['total_open'] == $status['total_closed'])
132
+		$status['ratio'] = '1:1';
133
+	elseif ($status['total_open'] == 0)
134
+		$status['ratio'] = '0:' . $status['total_closed'];
135
+	elseif ($status['total_closed'] == 0)
136
+		$status['ratio'] = $status['total_open'] . ':0';
137
+	elseif ($status['total_open'] > $status['total_closed'])
138
+		$status['ratio'] = round($status['total_open'] / $status['total_closed']) . ':1';
139
+	elseif ($status['total_open'] < $status['total_closed'])
140
+		$status['ratio'] = '1:' . round($status['total_closed'] / $status['total_open']);
141
+
142
+	return $status;
143
+}
144
+
145
+// This gets our ticket open/closed info.
146
+function shd_stats_today()
147
+{
148
+	global $smcFunc;
149
+
150
+	$actions = array(
151
+		// These are open tickets.
152
+		'newticket' => TICKET_STATUS_NEW,
153
+		'unresolve' => TICKET_STATUS_NEW,
154
+
155
+		// These are resolved tickets.
156
+		'resolve' => TICKET_STATUS_CLOSED,
157
+	);
158
+
159
+	$totals = array(
160
+		TICKET_STATUS_NEW => 0,
161
+		TICKET_STATUS_CLOSED => 0,
162
+	);
163
+
164
+	$request = $smcFunc['db_query']('', '
165
+		SELECT COUNT(la.id_ticket) AS count, t.status
166
+		FROM {db_prefix}helpdesk_log_action AS la
167
+			INNER JOIN {db_prefix}helpdesk_tickets AS t ON (la.id_ticket = t.id_ticket)
168
+		WHERE la.action IN ({array_string:actions})
169
+			AND la.log_time > {int:today}
170
+		GROUP BY la.action',
171
+		array(
172
+			'actions' => array_keys($actions),
173
+			// we could use strtotime from a date(n j Y), but this seems safer calculations
174
+			'today' => mktime(0, 0, 0, date('n'), date('j'), date('Y'))
175
+	));
176
+
177
+	while ($row = $smcFunc['db_fetch_assoc']($request))
178
+		$totals[$row['status']] = $row['count'];
179
+	$smcFunc['db_free_result']($request);
180
+
181
+	return $totals;
182
+}
183
+
184
+// This gets our ticket open/closed info.
185
+function shd_stats_most()
186
+{
187
+	global $smcFunc;
188
+
189
+	$actions = array(
190
+		TICKET_STATUS_NEW => array('newticket'),
191
+		TICKET_STATUS_CLOSED => array('resolve')
192
+	);
193
+
194
+	$most = array(
195
+		TICKET_STATUS_NEW => array(0, 0),
196
+		TICKET_STATUS_CLOSED => array(0, 0),
197
+	);
198
+
199
+	foreach ($actions as $action)
200
+	{
201
+		$request = $smcFunc['db_query']('', '
202
+			SELECT COUNT(la.id_ticket) AS count, t.status, log_time
203
+			FROM {db_prefix}helpdesk_log_action AS la
204
+				INNER JOIN {db_prefix}helpdesk_tickets AS t ON (la.id_ticket = t.id_ticket)
205
+			WHERE la.action IN ({array_string:actions})
206
+			GROUP BY unix_timestamp() - log_time < {int:24hrs} AND unix_timestamp() - log_time + {int:24hrs} > 0
207
+			ORDER BY count DESC
208
+			LIMIT 1',
209
+			array(
210
+				'actions' => $action,
211
+				'24hrs' => 86400,
212
+		));
213
+
214
+		while ($row = $smcFunc['db_fetch_assoc']($request))
215
+			$most[$row['status']] = array($row['count'], $row['log_time']);
216
+		$smcFunc['db_free_result']($request);
217
+	}
218
+
219
+	return $most;
220
+}
221
+
222
+// This gets our ticket open/closed info.
223
+function shd_stats_average()
224
+{
225
+	global $smcFunc;
226
+
227
+	$actions = array(
228
+		TICKET_STATUS_NEW => array('newticket', 'unresolve'),
229
+		TICKET_STATUS_CLOSED => array('resolve'),
230
+		TICKET_STATUS_PENDING_STAFF => array('assign'),
231
+	);
232
+
233
+	$average = array(
234
+		TICKET_STATUS_NEW => 0,
235
+		TICKET_STATUS_CLOSED => 0,
236
+		TICKET_STATUS_PENDING_STAFF => 0,
237
+	);
238
+
239
+	foreach ($actions as $action)
240
+		$request = $smcFunc['db_query']('', '
241
+			SELECT AVG(la.id_ticket) AS count, t.status
242
+			FROM {db_prefix}helpdesk_log_action AS la
243
+				INNER JOIN {db_prefix}helpdesk_tickets AS t ON (la.id_ticket = t.id_ticket)
244
+			WHERE la.action IN ({array_string:actions})
245
+			GROUP BY (la.id_ticket)',
246
+			array(
247
+				'actions' => $action,
248
+				'24hrs' => 86400,
249
+		));
250
+
251
+	while ($row = $smcFunc['db_fetch_assoc']($request))
252
+		$average[$row['status']] = $row['count'];
253
+	$smcFunc['db_free_result']($request);
254
+
255
+	return $average;
256
+}
257
+
258
+// This gets our user info.
259
+function shd_stats_totals()
260
+{
261
+	global $smcFunc;
262
+
263
+	// Count Admins separately for now.!
264
+	$admins = array();
265
+	if (empty($totals[ROLE_ADMIN]))
266
+	{
267
+		$request = $smcFunc['db_query']('', '
268
+			SELECT id_member
269
+			FROM {db_prefix}members
270
+			WHERE id_group = {int:admin} OR FIND_IN_SET({int:admin}, additional_groups)',
271
+			array(
272
+				'admin' => 1
273
+		));
274
+
275
+		while ($row = $smcFunc['db_fetch_assoc']($request))
276
+			$admins[] = $row['id_member'];
277
+
278
+		$smcFunc['db_free_result']($request);
279
+	}
280
+
281
+	// @TODO: This most likely will filesort and be slow on large helpdesks.
282
+	$request = $smcFunc['db_query']('', '
283
+		SELECT COUNT(mem.id_member) AS count, hdr.template
284
+		FROM {db_prefix}members AS mem
285
+			INNER JOIN {db_prefix}helpdesk_role_groups AS hdrg ON (mem.id_group = hdrg.id_group OR FIND_IN_SET(hdrg.id_group, mem.additional_groups))
286
+			INNER JOIN {db_prefix}helpdesk_roles AS hdr ON (hdrg.id_role = hdr.id_role)
287
+		WHERE mem.id_member NOT IN ({array_int:admins})
288
+		GROUP BY hdr.template',
289
+		array(
290
+			'admins' => $admins
291
+	));
292
+
293
+	$totals = array(
294
+		ROLE_USER => 0,
295
+		ROLE_STAFF => 0,
296
+//		ROLE_SUPERVISOR => 0,
297
+		ROLE_ADMIN => 0
298
+	);
299
+
300
+	while ($row = $smcFunc['db_fetch_assoc']($request))
301
+		$totals[$row['template']] = $row['count'];
302
+	$smcFunc['db_free_result']($request);
303
+
304
+	// Add in the admins.
305
+	$totals[ROLE_ADMIN] += count($admins);
306
+
307
+	return $totals;
308
+}
309
+
310
+// This gets our urgency.
311
+function shd_stats_urgency()
312
+{
313
+	global $smcFunc;
314
+
315
+	$urgency = array(
316
+		TICKET_URGENCY_LOW => 0,
317
+		TICKET_URGENCY_MEDIUM => 0,
318
+		TICKET_URGENCY_HIGH => 0,
319
+		TICKET_URGENCY_VHIGH => 0,
320
+		TICKET_URGENCY_SEVERE => 0,
321
+		TICKET_URGENCY_CRITICAL => 0,
322
+	);
323
+
324
+	// We reformat it now.
325
+	$urgency = array(
326
+		'open' => $urgency,
327
+		'closed' => $urgency
328
+	);
329
+
330
+	// Open or closed is all we need to know.
331
+	$status = array(
332
+		TICKET_STATUS_NEW => 'open',
333
+		TICKET_STATUS_PENDING_STAFF => 'open',
334
+		TICKET_STATUS_PENDING_USER => 'open',
335
+		TICKET_STATUS_CLOSED => 'closed',
336
+		TICKET_STATUS_WITH_SUPERVISOR => 'open',
337
+		TICKET_STATUS_ESCALATED => 'open',
338
+		TICKET_STATUS_DELETED => 'closed',
339
+	);
340
+
341
+	// Collect up some numbers.
342
+	$request = $smcFunc['db_query']('', '
343
+		SELECT COUNT(id_ticket) AS count, urgency, status
344
+		FROM {db_prefix}helpdesk_tickets
345
+		WHERE urgency IN ({array_string:urgency})
346
+		GROUP BY urgency, status',
347
+		array(
348
+			'urgency' => array_keys($urgency)
349
+	));
350
+
351
+	while ($row = $smcFunc['db_fetch_assoc']($request))
352
+		$urgency[$status[$row['status']]][$row['urgency']] = $row['count'];
353
+	$smcFunc['db_free_result']($request);
354
+
355
+	return $urgency;
356
+}
357
+
358
+// Time to become Archeologists.
359
+function shd_stats_history()
360
+{
361
+	global $smcFunc;
362
+
363
+	// What tasks we want to perform.
364
+	$tasks = array(
365
+		'open' => 0,
366
+		'resolved' => 0,
367
+		'assigned' => 0,
368
+		'reopen' => 0,
369
+		'child' => array(),
370
+	);
371
+
372
+	$conversion = array(
373
+		'newticket' => 'open',
374
+		'assign' => 'assigned',
375
+		'resolve' => 'resolved',
376
+	);
377
+
378
+	// @TODO: In future, use ajax to support selecting years/months totals
379
+	$request = $smcFunc['db_query']('', '
380
+		SELECT log_time, action
381
+		FROM {db_prefix}helpdesk_log_action');
382
+
383
+	$history = array();
384
+	while ($row = $smcFunc['db_fetch_assoc']($request))
385
+	{
386
+		list($year, $month, $day) = explode(' ', date('Y m d', $row['log_time']));
387
+
388
+		// If only we had to do this once a year.
389
+		if (!isset($history[$year]))
390
+			$history[$year] = $tasks;
391
+		$history[$year][$conversion[$row['action']]] += 1;
392
+
393
+		// Now we do the monthly stats
394
+		if (!isset($history[$year]['child'][$month]))
395
+			$history[$year]['child'][$month] = $tasks;
396
+		$history[$year]['child'][$month][$conversion[$row['action']]] += 1;
397
+
398
+		// For day we just simply do it.
399
+		if (!isset($history[$year]['child'][$month]['child'][$day]))
400
+			$history[$year]['child'][$month]['child'][$day] = $tasks;
401
+		$history[$year]['child'][$month]['child'][$day][$conversion[$row['action']]] += 1;
402
+	}
403
+	$smcFunc['db_free_result']($request);
404
+
405
+	// We now return to the history channel.
406
+	return $history;
407
+}
0 408
\ No newline at end of file
... ...
@@ -0,0 +1,80 @@
1
+<?php
2
+###########################################################
3
+#       Simple Desk Project - www.simpledesk.net          #
4
+###########################################################
5
+#     An advanced help desk modifcation built on SMF      #
6
+###########################################################
7
+#                                                         #
8
+#       * Copyright 2010 - SimpleDesk.net                 #
9
+#                                                         #
10
+# This file and its contents are subject to the license   #
11
+# included with this distribution, license.txt, which     #
12
+# states that this software is New BSD Licensed.          #
13
+# Any questions, please contact SimpleDesk.net            #
14
+#                                                         #
15
+###########################################################
16
+# SimpleDesk Version: 2.0 Anatidae                        #
17
+# File Info: index.php / 2.0 Anatidae                     #
18
+###########################################################
19
+
20
+/**
21
+ *	@package plugin-stats
22
+ *	@since 2.0
23
+*/
24
+
25
+if (!defined('SHD_VERSION'))
26
+	die('Hacking attempt...');
27
+
28
+/*
29
+ *	Return information about this plugin.
30
+ *
31
+ *	details
32
+ *	- name: a $txt reference for the plugin's name (so it can be translated), if not present as a $txt will be used as a literal. (Note, see includes - language below)
33
+ *	- description: a $txt reference one line description of the mod (translatable) - if not present, it will be used as a literal.
34
+ *	- author: Author's name, literal
35
+ *	- website: Website to link back to the author
36
+ *	- version: Plugin version
37
+ *	- compatibility: Array of supported SD version-strings
38
+ *
39
+ *	includes
40
+ *	- source: a key-value pair array of file names to include at strategic points, key name is the point to include it on, value is a filename or array of filenames to include within the plugin's dir
41
+ *	- language: a key-value pair of array of language files to include, much like source.
42
+ *
43
+ *	hooks
44
+ *	- key-value pair of hook name to function name or array of function names to be called at the hook point
45
+ *
46
+ *	@since 2.1
47
+*/
48
+function shdplugin_stats()
49
+{
50
+	return array(
51
+		'details' => array( // general plugin details
52
+			'title' => 'shdp_stats',
53
+			'description' => 'shdp_stats_desc',
54
+			'author' => 'SimpleDesk Team',
55
+			'website' => 'http://www.simpledesk.net/',
56
+			'version' => '1.0',
57
+			'compatibility' => array(
58
+				'SimpleDesk 2.0 Anatidae', // should tie up with the SHD_VERSION constants
59
+			),
60
+			'acp_url' => 'action=admin;area=helpdesk_options;sa=stats',
61
+		),
62
+		'includes' => array(
63
+			'source' => array(
64
+				'init' => 'SDPluginStats.php',
65
+			),
66
+			'language' => array(
67
+				'hdadmin' => 'SDPluginStats',
68
+				'helpdesk' => 'SDPluginStats',
69
+			),
70
+		),
71
+		'hooks' => array( // what functions to call when
72
+			'adminmenu' => 'shd_stats_adminmenu',
73
+			'hdadmininfo' => 'shd_stats_hdadmininfo',
74
+			'admin_admin' => 'shd_stats_admin',
75
+
76
+		),
77
+	);
78
+}
79
+
80
+
... ...
@@ -0,0 +1,216 @@
1
+<?php
2
+// Version: 2.0 Anatidae; SimpleDesk alternate front page template
3
+
4
+/**
5
+ *	This file handles the replacement front page.
6
+ *
7
+ *	@package plugin-frontpage
8
+ *	@since 2.0
9
+*/
10
+
11
+/**
12
+ *	Display the replacement front page.
13
+ *
14
+ *	@since 2.0
15
+*/
16
+function template_main()
17
+{
18
+	global $context, $txt, $settings, $scripturl;
19
+
20
+$txt['general_stats'] = 'General Statistics'; 
21
+$txt['new_tickets_today'] = 'New Tickets Today';
22
+$txt['closed_tickets_today'] = 'Closed Tickets Today';
23
+$txt['total_open_tickets'] = 'Total Open Tickets';
24
+$txt['total_closed_tickets'] = 'Total Closed Tickets';
25
+$txt['total_total_tickets'] = 'Total Tickets';
26
+$txt['most_open_tickets'] = 'Most Tickets Created';
27
+$txt['most_closed_tickets'] = 'Most Tickets Closed';
28
+$txt['average_new_tickets'] = 'Average New Tickets';
29
+$txt['average_closed_tickets'] = 'Average Closed Tickets';
30
+$txt['average_assign_tickets'] = 'Average Assigned Tickets';
31
+$txt['total_users'] = 'Total Users';
32
+$txt['total_staff'] = 'Total Staff';
33
+$txt['total_admins'] = 'Total Administrators';
34
+$txt['open_closed_ratio'] = 'Open/Closed Ratio';
35
+
36
+$txt['urgency_stats'] = 'Urgency Statistics';
37
+$txt['urgency_open'] = 'Open';
38
+$txt['urgency_closed'] = 'Closed';
39
+
40
+$txt['urgency_type_0'] = 'Low Urgency Tickets';
41
+$txt['urgency_type_1'] = 'Medium Urgency Tickets';
42
+$txt['urgency_type_2'] = 'High Urgency Tickets';
43
+$txt['urgency_type_3'] = 'Very High Urgency Tickets';
44
+$txt['urgency_type_4'] = 'Severe Urgency Tickets';
45
+$txt['urgency_type_5'] = 'Critical Urgency Tickets';
46
+
47
+$txt['ticket_history'] = 'Ticket History';
48
+$txt['yearly_summary'] = 'Yearly Summary';
49
+$txt['new_tickets'] = 'New Tickets';
50
+$txt['assigned_tickets'] = 'Assigned Tickets';
51
+$txt['reopen_tickets'] = 'Re-opened Tickets';
52
+$txt['closed_tickets'] = 'Closed Tickets';
53
+
54
+	echo '
55
+		<div class="title_bar">
56
+			<h4 class="titlebg">
57
+				<span class="ie6_header floatleft">
58
+					<img src="', $settings['images_url'], '/stats_info.gif" class="icon" alt="" /> ', $txt['general_stats'], '
59
+				</span>
60
+			</h4>
61
+		</div>
62
+		<div class="flow_hidden">
63
+			<div id="stats_left">
64
+				<div class="windowbg2">
65
+					<span class="topslice"><span></span></span>
66
+					<div class="content top_row">
67
+						<dl class="stats">
68
+							<dt>', $txt['new_tickets_today'], '</dt>
69
+							<dd>', $context['shd_stats']['today'][TICKET_STATUS_NEW], '</dd>
70
+							<dt>', $txt['closed_tickets_today'], '</dt>
71
+							<dd>', $context['shd_stats']['today'][TICKET_STATUS_CLOSED], '</dd>
72
+							<dt>', $txt['total_open_tickets'], '</dt>
73
+							<dd>', $context['shd_stats']['status']['total_open'], '</dd>
74
+							<dt>', $txt['total_closed_tickets'], '</dt>
75
+							<dd>', $context['shd_stats']['status']['total_closed'], '</dd>
76
+							<dt>', $txt['total_total_tickets'], '</dt>
77
+							<dd>', $context['shd_stats']['status']['total_total'], '</dd>
78
+							<dt>', $txt['most_open_tickets'], '</dt>
79
+							<dd>', $context['shd_stats']['most'][TICKET_STATUS_NEW][0], ' &mdash; ', date('F d, Y', $context['shd_stats']['most'][TICKET_STATUS_NEW][1]), '</dd>
80
+							<dt>', $txt['most_closed_tickets'], '</dt>
81
+							<dd>', $context['shd_stats']['most'][TICKET_STATUS_CLOSED][0], ' &mdash; ', date('F d, Y', $context['shd_stats']['most'][TICKET_STATUS_CLOSED][1]), '</dd>
82
+						</dl>
83
+						<div class="clear"></div>
84
+					</div>
85
+					<span class="botslice"><span></span></span>
86
+				</div>
87
+			</div>
88
+			<div id="stats_right">
89
+				<div class="windowbg2">
90
+					<span class="topslice"><span></span></span>
91
+					<div class="content top_row">
92
+						<dl class="stats">
93
+							<dt>', $txt['average_new_tickets'], ':</dt>
94
+							<dd>', $context['shd_stats']['average'][TICKET_STATUS_NEW], '</dd>
95
+							<dt>', $txt['average_closed_tickets'], ':</dt>
96
+							<dd>', $context['shd_stats']['average'][TICKET_STATUS_CLOSED], '</dd>
97
+							<dt>', $txt['average_assign_tickets'], ':</dt>
98
+							<dd>', $context['shd_stats']['average'][TICKET_STATUS_PENDING_STAFF], '</dd>
99
+							<dt>', $txt['total_users'], ':</dt>
100
+							<dd>', $context['shd_stats']['totals'][ROLE_USER], '</dd>
101
+							<dt>', $txt['total_staff'], ':</dt>
102
+							<dd>', $context['shd_stats']['totals'][ROLE_STAFF], '</dd>
103
+							<dt>', $txt['total_admins'], ':</dt>
104
+							<dd>', $context['shd_stats']['totals'][ROLE_ADMIN], '</dd>
105
+							<dt>', $txt['open_closed_ratio'], ':</dt>
106
+							<dd>', $context['shd_stats']['status']['ratio'], '</dd>
107
+						</dl>
108
+						<div class="clear"></div>
109
+					</div>
110
+					<span class="botslice"><span></span></span>
111
+				</div>
112
+			</div>
113
+		</div>
114
+
115
+		<div class="title_bar">
116
+			<h4 class="titlebg">
117
+				<span class="ie6_header floatleft">
118
+					<img src="', $settings['images_url'], '/stats_posters.gif" class="icon" alt="" /> ', $txt['urgency_stats'], '
119
+				</span>
120
+			</h4>
121
+		</div>
122
+		<div class="flow_hidden">
123
+			<div id="stats_left">
124
+				<div class="windowbg2">
125
+					<span class="topslice"><span></span></span>
126
+					<div class="content top_row">
127
+						<dl class="stats">';
128
+
129
+	foreach ($context['shd_stats']['urgency']['open'] as $type => $count)
130
+		echo '
131
+							<dt>', $txt['urgency_type_' . $type ], ' (', $txt['urgency_open'], ')</dt>
132
+							<dd>', $count, '</dd>';
133
+
134
+	echo '
135
+						</dl>
136
+						<div class="clear"></div>
137
+					</div>
138
+					<span class="botslice"><span></span></span>
139
+				</div>
140
+			</div>
141
+			<div id="stats_right">
142
+				<div class="windowbg2">
143
+					<span class="topslice"><span></span></span>
144
+					<div class="content top_row">
145
+						<dl class="stats">';
146
+
147
+	foreach ($context['shd_stats']['urgency']['closed'] as $type => $count)
148
+		echo '
149
+							<dt>', $txt['urgency_type_' . $type ], ' (', $txt['urgency_closed'], ')</dt>
150
+							<dd>', $count, '</dd>';
151
+
152
+	echo '
153
+						</dl>
154
+						<div class="clear"></div>
155
+					</div>
156
+					<span class="botslice"><span></span></span>
157
+				</div>
158
+			</div>
159
+		</div>
160
+		<div class="cat_bar">
161
+			<h3 class="catbg">
162
+				<span class="ie6_header floatleft">
163
+					<img src="', $settings['images_url'], '/stats_history.gif" class="icon" alt="" /> ', $txt['ticket_history'], '
164
+				</span>
165
+			</h3>
166
+		</div>
167
+		<table border="0" width="100%" cellspacing="1" cellpadding="4" class="table_grid" id="stats">
168
+			<thead>
169
+				<tr class="titlebg" valign="middle" align="center">
170
+					<th class="first_th lefttext" width="25%">', $txt['yearly_summary'], '</th>
171
+					<th width="15%">', $txt['new_tickets'], '</th>
172
+					<th width="15%">', $txt['assigned_tickets'], '</th>
173
+					<th width="15%">', $txt['reopen_tickets'], '</th>
174
+					<th class="last_th" width="15%">', $txt['closed_tickets'], '</th>
175
+				</tr>
176
+			</thead>
177
+			<tbody>';
178
+
179
+		foreach ($context['shd_stats']['history'] as $year_id => $year)
180
+		{
181
+			echo '
182
+				<tr class="windowbg2" valign="middle" align="center" id="year_', $id, '">
183
+					<th class="lefttext" width="25%">', $year_id, '</th>
184
+					<th width="15%">', $year['open'], '</th>
185
+					<th width="15%">', $year['assigned'], '</th>
186
+					<th width="15%">', $year['reopen'], '</th>
187
+					<th width="15%">', $year['resolved'], '</th>
188
+				</tr>';
189
+
190
+			foreach ($year['child'] as $month_id => $month)
191
+			{
192
+				echo '
193
+				<tr class="windowbg2" valign="middle" align="center" id="tr_month_', $month_id, '">
194
+					<th class="stats_month">', $txt['months_titles'][$month_id], ' ', $year_id, '</th>
195
+					<th width="15%">', $month['open'], '</th>
196
+					<th width="15%">', $month['assigned'], '</th>
197
+					<th width="15%">', $month['reopen'], '</th>
198
+					<th width="15%">', $month['resolved'], '</th>
199
+				</tr>';
200
+
201
+				foreach ($month['child'] as $day_id => $day)
202
+					echo '
203
+				<tr class="windowbg2" valign="middle" align="center" id="tr_day_', $year_id, '-', $month_id, '-', $day_id, '">
204
+					<td class="stats_day">', $year_id, '-', $month_id, '-', $day_id, '</td>
205
+					<th width="15%">', $day['open'], '</th>
206
+					<th width="15%">', $day['assigned'], '</th>
207
+					<th width="15%">', $day['reopen'], '</th>
208
+					<th width="15%">', $day['resolved'], '</th>
209
+				</tr>';
210
+			}
211
+		}
212
+
213
+		echo '
214
+			</tbody>
215
+		</table>';
216
+}
... ...
@@ -171,10 +171,8 @@ function shd_admin_info()
171 171
 			'title' => $txt['shd_admin_support'],
172 172
 		),
173 173
 	);
174
-	$_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subactions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'main';
175 174
 
176 175
 	$context[$context['admin_menu_name']]['tab_data'] = array(
177
-		'title' => '<img src="' . $settings['images_url'] . '/admin/shd/' . $subactions[$_REQUEST['sa']]['icon'] . '" class="icon" alt="*" />' . $subactions[$_REQUEST['sa']]['title'],
178 176
 		'description' => $txt['shd_admin_options_desc'],
179 177
 		'tabs' => array(
180 178
 			'main' => array(
... ...
@@ -189,6 +187,12 @@ function shd_admin_info()
189 187
 		),
190 188
 	);
191 189
 
190
+	call_integration_hook('shd_hook_hdadmininfo', array(&$subactions));
191
+	$_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subactions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'main';
192
+
193
+	// Now that we have validated the subaction.	
194
+	$context[$context['admin_menu_name']]['tab_data']['title'] = '<img src="' . $settings['images_url'] . '/admin/shd/' . $subactions[$_REQUEST['sa']]['icon'] . '" class="icon" alt="*" />' . $subactions[$_REQUEST['sa']]['title'];
195
+
192 196
 	// Are we doing the main page, or leaving here?
193 197
 	if (!empty($subactions[$_REQUEST['sa']]['function']))
194 198
 	{
... ...
@@ -363,6 +363,7 @@ function shd_list_hooks()
363 363
 		'shd_hook_helpdesk', // functions to run when starting the main helpdesk (before going off to subactions)
364 364
 		'shd_hook_hdadmin', // functions to run when starting the main SimpleDesk admin area (probably should include via _include_hdadmin)
365 365
 		'shd_hook_hdadminopts', // functions to run when working in the SimpleDesk options submenu structure (probably should include via _include_hdadmin)
366
+		'shd_hook_hdadmininfo', // functions to run when working in the SimpleDesk info submenu structure (probably should include via _include_hdadmin)
366 367
 		'shd_hook_hdadminoptssrch', // functions to run when setting up admin panel search for SimpleDesk options submenu structure (probably should include via _include_hdadmin)
367 368
 		'shd_hook_hdprofile', // functions to call when going into the helpdesk profile area
368 369
 	);
369 370