+ Tagging 1.0.1. It's not ready to be released yet, however.
nas

nas commited on 2010-12-04 01:57:02
Showing 100 changed files, with 13861 additions and 0 deletions.

... ...
@@ -0,0 +1,317 @@
1
+/* Half rounded headers */
2
+div.grid_header
3
+{
4
+	height: 28px;
5
+	margin-bottom: -1px;
6
+}
7
+/* You no left float, Core! */
8
+h3.grid_header img
9
+{
10
+	float: none;
11
+}
12
+
13
+/* This is done to position icons perfectly in the ticket header (i.e the navigation)*/
14
+h3.ticketheader img
15
+{
16
+	top: 2px;
17
+	position: relative;
18
+	margin-right: 2px;
19
+}
20
+
21
+.shd_hide_replybox
22
+{
23
+	position: relative;
24
+	top: 8px;
25
+}
26
+
27
+/* The Respond, Edit, Delete etc links */
28
+.shd_ticketlinks a
29
+{
30
+	margin-right: 10px;
31
+}
32
+.shd_ticketlinks a#last
33
+{
34
+	margin-right: 0px;
35
+}
36
+
37
+/* Ticket details (Left-floated box) */
38
+.shd_ticketdetails
39
+{
40
+	float: left;
41
+	width: 215px;
42
+	min-height: 280px;
43
+	overflow: auto;
44
+	margin-right: 20px;
45
+}
46
+.shd_ticketdetails ul
47
+{
48
+	list-style-type: none;
49
+	padding-left: 0;
50
+	margin: 0;
51
+}
52
+.shd_smallicon
53
+{
54
+	width: 13px;
55
+	height: 13px;
56
+	margin-right: 4px;
57
+}
58
+.shd_icon_minihead
59
+{
60
+	position: relative;
61
+	top: 3px;
62
+}
63
+.shd_tinyicon
64
+{
65
+	width: 9px;
66
+	height: 9px;
67
+}
68
+
69
+.shd_ticket_avatar
70
+{
71
+	margin: 1em auto;
72
+	text-align: center;
73
+}
74
+
75
+/* The ticket container */
76
+.shd_ticket
77
+{
78
+	min-height: 340px;
79
+}
80
+.shd_ticket_description
81
+{
82
+	padding-top: 9px;
83
+	padding-bottom: 45px;
84
+	margin-left: 260px;
85
+	min-height: 200px;
86
+}
87
+
88
+#shd_ticket_text
89
+{
90
+	min-height: 100px;
91
+}
92
+
93
+/* The big reply button */
94
+.shd_replybutton
95
+{
96
+	margin-top: 10px;
97
+	height: 15px;
98
+	background: url(../images/simpledesk/respond.png) no-repeat left;
99
+	background-position: 10px;
100
+	padding: 10px;
101
+	padding-left: 40px;
102
+	padding-top: 8px;
103
+	font-weight: bold;
104
+	font-size: 100%;
105
+	margin-bottom: 10px;
106
+}
107
+/* The big quote button */
108
+.shd_quotebutton
109
+{
110
+	margin-top: 10px;
111
+	height: 15px;
112
+	background: url(../images/simpledesk/quote.png) no-repeat left;
113
+	background-position: 10px;
114
+	padding: 10px;
115
+	padding-left: 40px;
116
+	padding-top: 8px;
117
+	font-weight: bold;
118
+	font-size: 100%;
119
+	margin-right: 10px;
120
+}
121
+/* Go advanced! */
122
+.shd_advancedbutton
123
+{
124
+	margin-top: 10px;
125
+	height: 15px;
126
+	background: url(../images/simpledesk/advanced.png) no-repeat left;
127
+	background-position: 10px;
128
+	padding: 10px;
129
+	padding-left: 40px;
130
+	padding-top: 8px;
131
+	font-weight: bold;
132
+	font-size: 100%;
133
+	margin-right: 10px;
134
+}
135
+/* Replies */
136
+.shd_reply
137
+{
138
+	padding-bottom: 32px;
139
+	padding-top: 12px;
140
+}
141
+.shd_posterinfo
142
+{
143
+	width: 14em;
144
+	text-align: center;
145
+	margin-left: 3px;
146
+	overflow: hidden;
147
+}
148
+.shd_postername
149
+{
150
+	font-size: 120%;
151
+}
152
+.shd_groupmargin
153
+{
154
+	margin-bottom: 10px;
155
+}
156
+.shd_posteravatar
157
+{
158
+	padding: 0px;
159
+	max-width: 50px;
160
+	max-height: 50px;
161
+}
162
+.shd_replyarea
163
+{
164
+	padding-left: 14em;
165
+	margin-left: 10px;
166
+}
167
+
168
+/* Attachments */
169
+.shd_replyattachments ul, .shd_replyattachments li
170
+{
171
+	display: inline;
172
+	list-style-type: none;
173
+}
174
+
175
+.shd_attachment
176
+{
177
+	padding-bottom: 5px;
178
+	font-size: 85%;
179
+}
180
+.shd_attachmentpreview
181
+{
182
+	text-align: center;
183
+}
184
+.shd_attachmentpreview img
185
+{
186
+	max-width: 75px;
187
+	margin: 3px;
188
+	margin-top: 6px;
189
+}
190
+.shd_attachmentpreview .description
191
+{
192
+	padding: 0;
193
+}
194
+.shd_removeattachment
195
+{
196
+	margin-left: -8px;
197
+	margin-right: 3px;
198
+}
199
+.shd_attachmentbox
200
+{
201
+	padding: 10px;
202
+}
203
+.shd_attachmentcolumn
204
+{
205
+	margin-right: 20px;
206
+}
207
+
208
+/* Ticket listing */
209
+.shd_ticketlist
210
+{
211
+	width: 100%;
212
+	border: solid #BBBBBB;
213
+	border-width: 1px 1px 0;
214
+}
215
+.shd_ticketlist td
216
+{
217
+	padding: 5px;
218
+	border-bottom: 1px solid #AFAFAF;
219
+}
220
+.shd_noticket
221
+{
222
+	text-align: center;
223
+}
224
+.shd_staffresources ul
225
+{
226
+	list-style-type: none;
227
+	padding: 0;
228
+	margin: 0;
229
+}
230
+.shd_staffresources li
231
+{
232
+	margin-bottom: 3px;
233
+}
234
+
235
+.shd_reply_attachments
236
+{
237
+	width: 68%;
238
+	margin-bottom: 0;
239
+	float: left;
240
+	margin-top: 5px;
241
+}
242
+
243
+.shd_modified
244
+{
245
+	margin-top: 20px;
246
+}
247
+
248
+.postreply
249
+{
250
+	margin: 0;
251
+}
252
+
253
+.postmodify
254
+{
255
+	margin: 0;
256
+}
257
+
258
+.shd_main_hd
259
+{
260
+	margin-top: -3px;
261
+}
262
+
263
+.shd_nowrap
264
+{
265
+	white-space: nowrap;
266
+}
267
+
268
+.pm_message
269
+{
270
+	margin-top: 1ex;
271
+}
272
+
273
+.shd_reply .avatar
274
+{
275
+	margin-bottom: 10px;
276
+}
277
+
278
+#quickReplyExpand
279
+{
280
+	margin-top: 8px;
281
+}
282
+
283
+/* The left column in the ticket display */
284
+.shd_ticket_leftcolumn
285
+{
286
+	width: 25%;
287
+}
288
+/* The right column in the ticket display */
289
+.shd_ticket_rightcolumn
290
+{
291
+	width: 75%;
292
+}
293
+
294
+/* The 'Empty log' link in the action log */
295
+.shd_empty_log
296
+{
297
+	font-size: 80%;
298
+}
299
+.shd_empty_log img
300
+{
301
+	position: relative;
302
+	top: 3px;
303
+	margin-right: 2px;
304
+}
305
+/* Override editor margins */
306
+#quickReplyOptions form textarea
307
+{
308
+	margin-bottom: 0!important;
309
+}
310
+
311
+.shd_gototicket
312
+{
313
+	display: block;
314
+	float: right;
315
+	margin-left: 1em;
316
+	white-space: nowrap;
317
+}
0 318
\ No newline at end of file
... ...
@@ -0,0 +1,89 @@
1
+#sd_live_news
2
+{
3
+	width: 64%;
4
+	font-size: 0.85em;
5
+}
6
+#sd_live_news div.content
7
+{
8
+	padding: 0;
9
+}
10
+#sd_live_news div.content dl
11
+{
12
+	padding: 0.5em 0 0 0.5em;
13
+}
14
+
15
+#sd_supportVersionsTable
16
+{
17
+	width: 34%;
18
+}
19
+#sd_version_details
20
+{
21
+	overflow: auto;
22
+	height: 16.4em;
23
+}
24
+#sdAnnouncements
25
+{
26
+	height: 21.6em;
27
+	overflow: auto;
28
+}
29
+#sdAnnouncements dt
30
+{
31
+	border-bottom: 1px dashed #000;
32
+}
33
+#sdAnnouncements dd
34
+{
35
+	padding: 0;
36
+	margin: 0 0 1em 1.5em;
37
+}
38
+#sd_update_section
39
+{
40
+	margin-top: 10px;
41
+	margin-bottom: 10px;
42
+}
43
+#sd_credits
44
+{
45
+}
46
+.sd_group_name
47
+{
48
+}
49
+.sd_group_name hr
50
+{
51
+	margin-top: 2px;
52
+}
53
+.shd_help
54
+{
55
+	float: right;
56
+	margin-top: 8px;
57
+}
58
+.shd_actionloginfo
59
+{
60
+	margin-bottom: 0;
61
+	border-bottom: 0;
62
+}
63
+.shd_admin_leftcolumn
64
+{
65
+	width: 74%;
66
+	margin-right: 10px;
67
+}
68
+.shd_admin_rightcolumn
69
+{
70
+	width: 24%;
71
+}
72
+.shd_credits_break
73
+{
74
+	margin-bottom: 0;
75
+
76
+}
77
+.shd_bbc_list
78
+{
79
+	width: 33%;
80
+}
81
+.shd_info_box
82
+{
83
+	margin-bottom: 0;
84
+}
85
+h3.sd_no_margin
86
+{
87
+	margin-bottom: 0px;
88
+	padding-bottom: 3px;
89
+}
0 90
\ No newline at end of file
... ...
@@ -0,0 +1,31 @@
1
+/* IE6, IE6, how do I hate thee, let me count the ways. */
2
+
3
+/* IE6 needs a height fix here */
4
+.shd_ticketdetails
5
+{
6
+	min-height: 280px;
7
+	height: auto !important;
8
+	height: 280px;
9
+}
10
+.shd_ticket
11
+{
12
+	min-height: 340px;
13
+	height: auto !important;
14
+	height: 340px;
15
+}
16
+
17
+/* For whatever odd reason this have to 0.8% or so smaller in IE6 to look good. */
18
+h3.grid_header
19
+{
20
+	width: 99.2%;
21
+}
22
+
23
+.shd_reply .shd_ticketlinks
24
+{
25
+	margin-top: -15px;
26
+}
27
+
28
+h3.ticketheader .shd_ticketlinks
29
+{
30
+	margin-top: 3px;
31
+}
0 32
\ No newline at end of file
... ...
@@ -0,0 +1,324 @@
1
+<?xml version="1.0"?>
2
+<!DOCTYPE modification SYSTEM "http://www.simplemachines.org/xml/modification">
3
+<modification xmlns="http://www.simplemachines.org/xml/modification" xmlns:smf="http://www.simplemachines.org/">
4
+	<id>SimpleDeskTeam:SimpleDesk</id>
5
+	<version>1.0 Felidae</version>
6
+	
7
+	<file name="$sourcedir/Display.php">
8
+		<operation><!-- add the option to the download system to ensure it handles SD attachments -->
9
+			<search position="after"><![CDATA[
10
+	// This is just a regular attachment...
11
+	else]]></search>
12
+			<add><![CDATA[
13
+	elseif (!empty($_REQUEST['ticket']))
14
+	{
15
+		// Make sure the attachment is on this ticket, note right now we're forcing it to be "approved"
16
+		$request = shd_db_query('', '
17
+			SELECT a.id_folder, a.filename, a.file_hash, a.fileext, a.id_attach, a.attachment_type, a.mime_type, 1 AS approved
18
+			FROM {db_prefix}attachments AS a
19
+				INNER JOIN {db_prefix}helpdesk_attachments AS hda ON (a.id_attach = hda.id_attach)
20
+				INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hda.id_msg = hdtr.id_msg)
21
+				INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hdtr.id_ticket = hdt.id_ticket)
22
+			WHERE {query_see_ticket}
23
+				AND a.id_attach = {int:attach}
24
+				AND hdt.id_ticket = {int:ticket}
25
+			LIMIT 1',
26
+			array(
27
+				'attach' => $_REQUEST['attach'],
28
+				'ticket' => (int) $_REQUEST['ticket'],
29
+			)
30
+		);
31
+	}]]></add>
32
+		</operation>
33
+	</file>
34
+
35
+	<file name="$sourcedir/ManageAttachments.php">
36
+		<operation><!-- extend removeAttachments to cover SD's table items since it's doing the rest of the work anyway -->
37
+			<search position="after"><![CDATA[	if ($return_affected_messages)]]></search>
38
+			<add><![CDATA[	// And now lastly to remove SimpleDesk attachments
39
+	if (!empty($attach))
40
+		$smcFunc['db_query']('', '
41
+			DELETE FROM {db_prefix}helpdesk_attachments
42
+			WHERE id_attach IN ({array_int:attachment_list})',
43
+			array(
44
+				'attachment_list' => $attach,
45
+			)
46
+		);
47
+
48
+]]></add>
49
+		</operation>
50
+		<operation><!-- prevent attachment maintenance killing our attachments! unconventionally, this is an atomic query for good reason, it MUST be put in place fully -->
51
+			<search position="replace"><![CDATA[SELECT a.id_attach, a.id_folder, a.filename, a.file_hash
52
+				FROM {db_prefix}attachments AS a
53
+					LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
54
+				WHERE a.id_attach BETWEEN {int:substep} AND {int:substep} + 499
55
+					AND a.id_member = {int:no_member}
56
+					AND a.id_msg != {int:no_msg}
57
+					AND m.id_msg IS NULL]]></search>
58
+			<add><![CDATA[SELECT a.id_attach, a.id_folder, a.filename, a.file_hash
59
+				FROM {db_prefix}attachments AS a
60
+					LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
61
+					LEFT JOIN {db_prefix}helpdesk_attachments AS hda ON (hda.id_attach = a.id_attach)
62
+					LEFT JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hdtr.id_msg = hda.id_msg)
63
+				WHERE a.id_attach BETWEEN {int:substep} AND {int:substep} + 499
64
+					AND a.id_member = {int:no_member}
65
+					AND a.id_msg != {int:no_msg}
66
+					AND m.id_msg IS NULL
67
+					AND hdtr.id_msg IS NULL]]></add>
68
+		</operation>
69
+		<operation><!-- override the master attachments code if it's HD only stuff requested -->
70
+			<search position="before"><![CDATA[$context['browse_type'] = isset($_REQUEST['avatars']) ? 'avatars' : (isset($_REQUEST['thumbs']) ? 'thumbs' : 'attachments');]]></search>
71
+			<add><![CDATA[
72
+
73
+	if (!empty($modSettings['helpdesk_active']) && (isset($_REQUEST['shd_attach']) || isset($_REQUEST['shd_thumb'])))
74
+	{
75
+		// We're actually wanting helpdesk only stuff, so do it and bail
76
+		return shd_admin_browse_attachments();
77
+	}
78
+
79
+]]></add>
80
+		</operation>
81
+		<operation><!-- add the code to deal with deleting our attachments from ACP -->
82
+			<search position="after"><![CDATA[if ($_REQUEST['type'] == 'avatars' && !empty($attachments))]]></search>
83
+			<add><![CDATA[if (in_array($_REQUEST['type'], array('shd_attach', 'shd_thumb')) && !empty($attachments))
84
+			$messages = removeAttachments(array('id_attach' => $attachments), '', true);
85
+		else]]></add>
86
+		</operation>
87
+		<operation><!-- add our handling for attachments -->
88
+			<search position="end" />
89
+			<add><![CDATA[
90
+
91
+function shd_admin_browse_attachments()
92
+{
93
+	global $context, $txt, $scripturl, $options, $modSettings;
94
+	global $smcFunc, $sourcedir;
95
+
96
+	$context['browse_type'] = isset($_REQUEST['shd_attach']) ? 'shd_attach' : 'shd_thumb';
97
+
98
+	// Set the options for the list component.
99
+	$listOptions = array(
100
+		'id' => 'file_list',
101
+		'title' => $txt['attachment_manager_' . $context['browse_type']],
102
+		'items_per_page' => $modSettings['defaultMaxMessages'],
103
+		'base_href' => $scripturl . '?action=admin;area=manageattachments;sa=browse;' . $context['browse_type'],
104
+		'default_sort_col' => 'name',
105
+		'no_items_label' => $txt['attachment_manager_' . $context['browse_type'] . '_no_entries'],
106
+		'get_items' => array(
107
+			'function' => 'shd_list_get_files',
108
+			'params' => array(
109
+				$context['browse_type'],
110
+			),
111
+		),
112
+		'get_count' => array(
113
+			'function' => 'shd_list_get_num_files',
114
+			'params' => array(
115
+				$context['browse_type'],
116
+			),
117
+		),
118
+		'columns' => array(
119
+			'name' => array(
120
+				'header' => array(
121
+					'value' => $txt['attachment_name'],
122
+				),
123
+				'data' => array(
124
+					'function' => create_function('$rowData', '
125
+						global $modSettings, $context, $scripturl;
126
+
127
+						$link = \'<a href="\';
128
+
129
+						$link .= sprintf(\'%1$s?action=dlattach;ticket=%2$d.0;id=%3$d\', $scripturl, $rowData[\'id_ticket\'], $rowData[\'id_attach\']);
130
+
131
+						$link .= \'"\';
132
+
133
+						// Show a popup on click if it\'s a picture and we know its dimensions.
134
+						if (!empty($rowData[\'width\']) && !empty($rowData[\'height\']))
135
+							$link .= sprintf(\' onclick="return reqWin(this.href + \\\';image\\\', %1$d, %2$d, true);"\', $rowData[\'width\'] + 20, $rowData[\'height\'] + 20);
136
+
137
+						$link .= sprintf(\'>%1$s</a>\', preg_replace(\'~&amp;#(\\\\d{1,7}|x[0-9a-fA-F]{1,6});~\', \'&#\\\\1;\', htmlspecialchars($rowData[\'filename\'])));
138
+
139
+						// Show the dimensions.
140
+						if (!empty($rowData[\'width\']) && !empty($rowData[\'height\']))
141
+							$link .= sprintf(\' <span class="smalltext">%1$dx%2$d</span>\', $rowData[\'width\'], $rowData[\'height\']);
142
+
143
+						return $link;
144
+					'),
145
+				),
146
+				'sort' => array(
147
+					'default' => 'a.filename',
148
+					'reverse' => 'a.filename DESC',
149
+				),
150
+			),
151
+			'filesize' => array(
152
+				'header' => array(
153
+					'value' => $txt['attachment_file_size'],
154
+				),
155
+				'data' => array(
156
+					'function' => create_function('$rowData','
157
+						global $txt;
158
+
159
+						return sprintf(\'%1$s%2$s\', round($rowData[\'size\'] / 1024, 2), $txt[\'kilobyte\']);
160
+					'),
161
+					'class' => 'windowbg',
162
+				),
163
+				'sort' => array(
164
+					'default' => 'a.size',
165
+					'reverse' => 'a.size DESC',
166
+				),
167
+			),
168
+			'member' => array(
169
+				'header' => array(
170
+					'value' => $txt['posted_by'],
171
+				),
172
+				'data' => array(
173
+					'function' => create_function('$rowData', '
174
+						global $scripturl;
175
+
176
+						return htmlspecialchars($rowData[\'poster_name\']);
177
+					'),
178
+				),
179
+				'sort' => array(
180
+					'default' => 'mem.real_name',
181
+					'reverse' => 'mem.real_name DESC',
182
+				),
183
+			),
184
+			'date' => array(
185
+				'header' => array(
186
+					'value' => $txt['date'],
187
+				),
188
+				'data' => array(
189
+					'function' => create_function('$rowData', '
190
+						global $txt, $context, $scripturl;
191
+
192
+						return empty($rowData[\'poster_time\']) ? $txt[\'never\'] : timeformat($rowData[\'poster_time\']);
193
+						'),
194
+					'class' => 'windowbg',
195
+				),
196
+				'sort' => array(
197
+					'default' => 'hdtr.id_msg',
198
+					'reverse' => 'hdtr.id_msg DESC',
199
+				),
200
+			),
201
+			'downloads' => array(
202
+				'header' => array(
203
+					'value' => $txt['downloads'],
204
+				),
205
+				'data' => array(
206
+					'function' => create_function('$rowData','
207
+						global $txt;
208
+
209
+						return comma_format($rowData[\'downloads\']);
210
+					'),
211
+					'class' => 'windowbg',
212
+				),
213
+				'sort' => array(
214
+					'default' => 'a.downloads',
215
+					'reverse' => 'a.downloads DESC',
216
+				),
217
+			),
218
+			'check' => array(
219
+				'header' => array(
220
+					'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" class="input_check" />',
221
+				),
222
+				'data' => array(
223
+					'sprintf' => array(
224
+						'format' => '<input type="checkbox" name="remove[%1$d]" class="input_check" />',
225
+						'params' => array(
226
+							'id_attach' => false,
227
+						),
228
+					),
229
+					'style' => 'text-align: center',
230
+				),
231
+			),
232
+		),
233
+		'form' => array(
234
+			'href' => $scripturl . '?action=admin;area=manageattachments;sa=remove;' . $context['browse_type'],
235
+			'include_sort' => true,
236
+			'include_start' => true,
237
+			'hidden_fields' => array(
238
+				'type' => $context['browse_type'],
239
+			),
240
+		),
241
+		'additional_rows' => array(
242
+			array(
243
+				'position' => 'below_table_data',
244
+				'value' => '<input type="submit" name="remove_submit" class="button_submit" value="' . $txt['quickmod_delete_selected'] . '" onclick="return confirm(\'' . $txt['confirm_delete_attachments'] . '\');" />',
245
+				'class' => 'titlebg',
246
+				'style' => 'text-align: right;',
247
+			),
248
+		),
249
+	);
250
+
251
+	// Create the list.
252
+	require_once($sourcedir . '/Subs-List.php');
253
+	createList($listOptions);
254
+}
255
+
256
+function shd_list_get_files($start, $items_per_page, $sort, $browse_type)
257
+{
258
+	global $smcFunc, $txt;
259
+
260
+	$request = $smcFunc['db_query']('', '
261
+		SELECT
262
+			hdtr.id_msg, IFNULL(mem.real_name, hdtr.poster_name) AS poster_name, hdtr.poster_time, hdt.id_ticket, hdtr.id_member,
263
+			a.id_attach, a.filename, a.file_hash, a.attachment_type, a.size, a.width, a.height, a.downloads, hdt.subject
264
+		FROM {db_prefix}attachments AS a
265
+			INNER JOIN {db_prefix}helpdesk_attachments AS hda ON (a.id_attach = hda.id_attach)
266
+			INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hda.id_ticket = hdt.id_ticket)
267
+			INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hda.id_msg = hdtr.id_msg)
268
+			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = hdtr.id_member)
269
+		WHERE a.attachment_type = {int:attachment_type}
270
+		ORDER BY {raw:sort}
271
+		LIMIT {int:start}, {int:per_page}',
272
+		array(
273
+			'attachment_type' => $browse_type === 'shd_thumb' ? '3' : '0',
274
+			'sort' => $sort,
275
+			'start' => $start,
276
+			'per_page' => $items_per_page,
277
+		)
278
+	);
279
+	$files = array();
280
+	while ($row = $smcFunc['db_fetch_assoc']($request))
281
+		$files[] = $row;
282
+	$smcFunc['db_free_result']($request);
283
+
284
+	return $files;
285
+}
286
+
287
+function shd_list_get_num_files($browse_type)
288
+{
289
+	global $smcFunc;
290
+
291
+	$request = $smcFunc['db_query']('', '
292
+		SELECT COUNT(*) AS num_attach
293
+		FROM {db_prefix}attachments AS a
294
+			INNER JOIN {db_prefix}helpdesk_attachments AS hda ON (a.id_attach = hda.id_attach)
295
+			INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hda.id_ticket = hdt.id_ticket)
296
+			INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hda.id_msg = hdtr.id_msg)
297
+		WHERE a.attachment_type = {int:attachment_type}
298
+			AND a.id_member = {int:guest_id_member}',
299
+		array(
300
+			'attachment_type' => $browse_type === 'shd_thumb' ? '3' : '0',
301
+			'guest_id_member' => 0,
302
+		)
303
+	);
304
+
305
+	list ($num_files) = $smcFunc['db_fetch_row']($request);
306
+	$smcFunc['db_free_result']($request);
307
+
308
+	return $num_files;
309
+}
310
+
311
+]]></add>
312
+		</operation>
313
+	</file>
314
+
315
+	<file name="$themedir/ManageAttachments.template.php">
316
+		<operation>
317
+			<search position="before"><![CDATA[$txt['attachment_manager_thumbs'], ']]></search>
318
+			<add><![CDATA[', (empty($GLOBALS['modSettings']['helpdesk_active']) ? '' : '</a> |
319
+				<a href="' . $scripturl . '?action=admin;area=manageattachments;sa=browse;shd_attach">' . ($context['browse_type'] == 'shd_attach' ? '<img src="' . $settings['images_url'] . '/selected.gif" alt="&gt;" border="0" /> ' : '') .  $txt['attachment_manager_shd_attach'] . '</a> |
320
+				<a href="' . $scripturl . '?action=admin;area=manageattachments;sa=browse;shd_thumb">' . ($context['browse_type'] == 'shd_thumb' ? '<img src="' . $settings['images_url'] . '/selected.gif" alt="&gt;" border="0" /> ' : '') . $txt['attachment_manager_shd_thumb']), ']]></add>
321
+		</operation>
322
+	</file>
323
+
324
+</modification>
0 325
\ No newline at end of file
... ...
@@ -0,0 +1,177 @@
1
+<?xml version="1.0"?>
2
+<!DOCTYPE modification SYSTEM "http://www.simplemachines.org/xml/modification">
3
+<modification xmlns="http://www.simplemachines.org/xml/modification" xmlns:smf="http://www.simplemachines.org/">
4
+	<id>SimpleDeskTeam:SimpleDesk</id>
5
+	<version>1.0 Felidae</version>
6
+	
7
+	<file name="$sourcedir/Profile-View.php">
8
+		<operation><!-- add the code to call for IP tracking -->
9
+			<search position="after"><![CDATA[	$context['single_ip'] = strpos($context['ip'], '*') === false;]]></search>
10
+			<add><![CDATA[	// Set the options for the helpdesk replies list.
11
+	if (!empty($modSettings['helpdesk_active']))
12
+	{
13
+		$listOptions = array(
14
+			'id' => 'track_helpdesk_list',
15
+			'title' => $txt['shd_replies_from_ip'] . ' ' . $context['ip'],
16
+			'start_var_name' => 'helpdeskStart',
17
+			'items_per_page' => $modSettings['defaultMaxMessages'],
18
+			'no_items_label' => $txt['shd_replies_from_ip'],
19
+			'base_href' => $context['base_url'] . ';searchip=' . $context['ip'],
20
+			'default_sort_col' => 'date2',
21
+			'get_items' => array(
22
+				'function' => 'shd_list_get_ip_messages',
23
+				'params' => array(
24
+					'hdtr.poster_ip ' . $ip_string,
25
+					array('ip_address' => $ip_var),
26
+				),
27
+			),
28
+			'get_count' => array(
29
+				'function' => 'shd_list_get_ip_message_count',
30
+				'params' => array(
31
+					'hdtr.poster_ip ' . $ip_string,
32
+					array('ip_address' => $ip_var),
33
+				),
34
+			),
35
+			'columns' => array(
36
+				'ip_address2' => array(
37
+					'header' => array(
38
+						'value' => $txt['ip_address'],
39
+					),
40
+					'data' => array(
41
+						'sprintf' => array(
42
+							'format' => '<a href="' . $context['base_url'] . ';searchip=%1$s">%1$s</a>',
43
+							'params' => array(
44
+								'ip' => false,
45
+							),
46
+						),
47
+					),
48
+					'sort' => array(
49
+						'default' => 'INET_ATON(hdtr.poster_ip)',
50
+						'reverse' => 'INET_ATON(hdtr.poster_ip) DESC',
51
+					),
52
+				),
53
+				'display_name' => array(
54
+					'header' => array(
55
+						'value' => $txt['display_name'],
56
+					),
57
+					'data' => array(
58
+						'db' => 'member_link',
59
+					),
60
+				),
61
+				'subject' => array(
62
+					'header' => array(
63
+						'value' => $txt['subject'],
64
+					),
65
+					'data' => array(
66
+						'sprintf' => array(
67
+							'format' => '<a href="' . $scripturl . '?action=helpdesk;sa=ticket;ticket=%1$s.msg%2$s#msg%2$s" rel="nofollow">%3$s</a>%4$s',
68
+							'params' => array(
69
+								'ticket' => false,
70
+								'id' => false,
71
+								'subject' => false,
72
+								'additional' => false,
73
+							),
74
+						),
75
+					),
76
+				),
77
+				'date2' => array(
78
+					'header' => array(
79
+						'value' => $txt['date'],
80
+					),
81
+					'data' => array(
82
+						'db' => 'time',
83
+					),
84
+					'sort' => array(
85
+						'default' => 'hdtr.id_msg DESC',
86
+						'reverse' => 'hdtr.id_msg',
87
+					),
88
+				),
89
+			),
90
+			'additional_rows' => array(
91
+				array(
92
+					'position' => 'after_title',
93
+					'value' => '<span class="smalltext">' . $txt['shd_replies_from_ip_desc'] . '</span>',
94
+					'class' => 'windowbg',
95
+					'style' => 'padding: 2ex;',
96
+				),
97
+			),
98
+		);
99
+
100
+		// Create the helpdesk replies list.
101
+		createList($listOptions);
102
+	}
103
+
104
+]]></add>
105
+		</operation>
106
+		<operation><!-- add the code to get IP tracking data -->
107
+			<search position="end" />
108
+			<add><![CDATA[
109
+
110
+function shd_list_get_ip_messages($start, $items_per_page, $sort, $where, $where_vars = array())
111
+{
112
+	global $smcFunc, $txt, $scripturl;
113
+	$query = shd_db_query('', '
114
+		SELECT
115
+			hdtr.id_msg, hdtr.poster_ip, IFNULL(mem.real_name, hdtr.poster_name) AS display_name, mem.id_member,
116
+			hdt.subject, hdtr.poster_time, hdt.id_ticket, hdt.id_first_msg
117
+		FROM {db_prefix}helpdesk_ticket_replies AS hdtr
118
+			INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hdtr.id_ticket = hdt.id_ticket)
119
+			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = hdtr.id_member)
120
+		WHERE {query_see_ticket} AND ' . $where . '
121
+		ORDER BY ' . $sort . '
122
+		LIMIT ' . $start . ', ' . $items_per_page,
123
+		array_merge($where_vars, array(
124
+		))
125
+	);
126
+	$messages = array();
127
+	while ($row = $smcFunc['db_fetch_assoc']($query))
128
+		$messages[] = array(
129
+			'ip' => $row['poster_ip'],
130
+			'member_link' => shd_profile_link($row['display_name'], $row['id_member']),
131
+			'ticket' => $row['id_ticket'],
132
+			'id' => $row['id_msg'],
133
+			'subject' => $row['subject'],
134
+			'time' => timeformat($row['poster_time']),
135
+			'timestamp' => forum_time(true, $row['poster_time']),
136
+			'additional' => $row['id_first_msg'] == $row['id_msg'] ? $txt['shd_is_ticket_opener'] : '',
137
+		);
138
+	$smcFunc['db_free_result']($query);
139
+
140
+	return $messages;
141
+}
142
+
143
+function shd_list_get_ip_message_count($where, $where_vars = array())
144
+{
145
+	global $smcFunc;
146
+
147
+	$request = shd_db_query('', '
148
+		SELECT COUNT(id_msg) AS message_count
149
+		FROM {db_prefix}helpdesk_ticket_replies AS hdtr
150
+			INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hdtr.id_ticket = hdt.id_ticket)
151
+		WHERE {query_see_ticket} AND ' . $where,
152
+		$where_vars
153
+	);
154
+	list ($count) = $smcFunc['db_fetch_row']($request);
155
+	$smcFunc['db_free_result']($request);
156
+
157
+	return $count;
158
+}
159
+
160
+]]></add>
161
+		</operation>
162
+	</file>
163
+
164
+	<file name="$themedir/Profile.template.php"><!-- add the display of tracked IPs to the profile view -->
165
+		<operation>
166
+			<search position="before"><![CDATA[template_show_list('track_message_list');]]></search>
167
+			<add><![CDATA[
168
+	global $modSettings;
169
+	if (!empty($modSettings['helpdesk_active']))
170
+	{
171
+		echo '<br />';
172
+
173
+		template_show_list('track_helpdesk_list');
174
+	}]]></add>
175
+		</operation>
176
+	</file>
177
+</modification>
0 178
\ No newline at end of file
... ...
@@ -0,0 +1,505 @@
1
+<?xml version="1.0"?>
2
+<!DOCTYPE modification SYSTEM "http://www.simplemachines.org/xml/modification">
3
+<modification xmlns="http://www.simplemachines.org/xml/modification" xmlns:smf="http://www.simplemachines.org/">
4
+	<id>SimpleDeskTeam:SimpleDesk</id>
5
+	<version>1.0 Felidae</version>
6
+	
7
+	<file name="$sourcedir/Admin.php"><!-- adds the navbar links to the admin panel -->
8
+		<operation><!-- main SHD menu -->
9
+			<search position="after"><![CDATA[	// Make sure the administrator has a valid session...]]></search>
10
+			<add><![CDATA[	// Add the SHD item to the menu
11
+	$admin_areas['helpdesk_info'] = array(
12
+		'title' => $txt['shd_helpdesk'],
13
+		'permission' => array('admin_forum', 'admin_helpdesk'),
14
+		'enabled' => !empty($modSettings['helpdesk_active']),
15
+		'areas' => array(
16
+			'helpdesk_info' => array(
17
+				'label' => $txt['shd_admin_info'],
18
+				'file' => 'SimpleDesk-Admin.php',
19
+				'icon' => 'simpledesk.png',
20
+				'function' => 'shd_admin_main',
21
+				'subsections' => array(
22
+				),
23
+			),
24
+			'helpdesk_options' => array(
25
+				'label' => $txt['shd_admin_options'],
26
+				'file' => 'SimpleDesk-Admin.php',
27
+				'icon' => 'shd_status.png',
28
+				'function' => 'shd_admin_main',
29
+				'subsections' => array(
30
+					'display' => array($txt['shd_admin_options_display'], array('admin_forum', 'admin_helpdesk')),
31
+					'posting' => array($txt['shd_admin_options_posting'], array('admin_forum', 'admin_helpdesk')),
32
+					'admin' => array($txt['shd_admin_options_admin'], array('admin_forum', 'admin_helpdesk')),
33
+					'standalone' => array($txt['shd_admin_options_standalone'], array('admin_forum', 'admin_helpdesk')),
34
+				),
35
+			),
36
+			'helpdesk_actionlog' => array(
37
+				'label' => $txt['shd_admin_actionlog'],
38
+				'file' => 'SimpleDesk-Admin.php',
39
+				'icon' => 'shd_log.png',
40
+				'function' => 'shd_admin_main',
41
+				'subsections' => array(
42
+				),
43
+			),
44
+			'helpdesk_support' => array(
45
+				'label' => $txt['shd_admin_support'],
46
+				'file' => 'SimpleDesk-Admin.php',
47
+				'icon' => 'shd_support.png',
48
+				'function' => 'shd_admin_main',
49
+				'subsections' => array(
50
+				),
51
+			),
52
+		),
53
+	);
54
+
55
+]]></add>
56
+		</operation>
57
+		<operation><!-- add the item to the logs menu -->
58
+			<search position="before"><![CDATA['pruning' => array($txt['pruning_title'], 'admin_forum'),]]></search>
59
+			<add><![CDATA[
60
+						'helpdesklog' => array($txt['shd_admin_helpdesklog'], 'admin_forum', 'enabled' => $modSettings['helpdesk_active'], 'url' => $scripturl . '?action=admin;area=helpdesk_actionlog'),]]></add>
61
+		</operation>
62
+		<operation><!-- load the SD admin language file -->
63
+			<search position="before"><![CDATA[loadLanguage('Admin');]]></search>
64
+			<add><![CDATA[
65
+	shd_load_language('SimpleDeskAdmin');]]></add>
66
+		</operation>
67
+		<operation><!-- add SimpleDesk-Admin to the list of files to load when doing admin search -->
68
+			<search position="after"><![CDATA[	foreach ($include_files as $file)]]></search>
69
+			<add><![CDATA[	// Add SimpleDesk's file before we start anything
70
+	$include_files[] = 'SimpleDesk-Admin';
71
+
72
+]]></add>
73
+		</operation>
74
+		<operation><!-- prepend SimpleDesk's ACP helper functions to the list of places to search -->
75
+			<search position="replace"><![CDATA[// This is a special array of functions that contain setting data - we query all these to simply pull all setting bits!
76
+	$settings_search = array(]]></search>
77
+			<add><![CDATA[// Add SimpleDesk functions
78
+	$settings_search = array();
79
+	$settings_search[] = array('shd_modify_display_options', 'area=helpdesk_options;sa=display');
80
+	$settings_search[] = array('shd_modify_posting_options', 'area=helpdesk_options;sa=posting');
81
+	$settings_search[] = array('shd_modify_admin_options', 'area=helpdesk_options;sa=admin');
82
+	$settings_search[] = array('shd_modify_standalone_options', 'area=helpdesk_options;sa=standalone');
83
+
84
+	// This is a special array of functions that contain setting data - we query all these to simply pull all setting bits!
85
+	$settings_search += array(
86
+		4 =>]]></add>
87
+		</operation>
88
+	</file>
89
+	
90
+	<file name="$sourcedir/Load.php"><!-- load the main SimpleDesk language strings -->
91
+		<operation>
92
+			<search position="before"><![CDATA[loadSubTemplate('init', 'ignore');]]></search>
93
+			<add><![CDATA[
94
+	// Load the language strings
95
+	shd_load_language('SimpleDesk');]]></add>
96
+		</operation>
97
+		<operation><!-- globally figure out if Helpdesk is enabled; note this doesn't get pushed into settings table :) -->
98
+			<search position="after"><![CDATA[	// Is post moderation alive and well?]]></search>
99
+			<add><![CDATA[	// Are we helping people today?
100
+	$modSettings['helpdesk_active'] = isset($modSettings['admin_features']) ? in_array('shd', explode(',', $modSettings['admin_features'])) : false;
101
+
102
+]]></add>
103
+		</operation>
104
+	</file>
105
+	
106
+	<file name="$sourcedir/Subs.php"><!-- reconfigure the main menu somewhat -->
107
+		<operation><!-- add a generic menu item -->
108
+			<search position="replace"><![CDATA[			),
109
+			'profile' => array(]]></search>
110
+			<add><![CDATA[			),
111
+			'helpdesk' => array(
112
+				'title' => $modSettings['helpdesk_active'] && SMF != 'SSI' ? shd_get_active_tickets() : $txt['shd_helpdesk'],
113
+				'href' => $scripturl . '?action=helpdesk;sa=main',
114
+				'show' => $modSettings['helpdesk_active'] && (allowedTo('access_helpdesk') || allowedTo('admin_helpdesk')),
115
+				'sub_buttons' => array(
116
+					'newticket' => array(
117
+						'title' => $txt['shd_new_ticket'],
118
+						'href' => $scripturl . '?action=helpdesk;sa=newticket',
119
+						'show' => SMF == 'SSI' ? false : shd_allowed_to('shd_new_ticket'),
120
+					),
121
+					'closedtickets' => array(
122
+						'title' => $txt['shd_tickets_closed'],
123
+						'href' => $scripturl . '?action=helpdesk;sa=closedtickets',
124
+						'show' => SMF == 'SSI' ? false : (shd_allowed_to('shd_resolve_ticket_own') || shd_allowed_to('shd_resolve_ticket_any')),
125
+					),			
126
+					'recyclebin' => array(
127
+						'title' => $txt['shd_recycle_bin'],
128
+						'href' => $scripturl . '?action=helpdesk;sa=recyclebin',
129
+						'show' => SMF == 'SSI' ? false : shd_allowed_to('shd_access_recyclebin'),
130
+					),					
131
+				),
132
+			),
133
+			'profile' => array(]]></add>
134
+		</operation>
135
+		<operation><!-- turn off PMs if they're disabled -->
136
+			<search position="replace"><![CDATA[$context['allow_pm'] = allowedTo('pm_read');]]></search>
137
+			<add><![CDATA[$context['allow_pm'] = allowedTo('pm_read') && (empty($modSettings['helpdesk_active']) || empty($modSettings['shd_helpdesk_only']) || empty($modSettings['shd_disable_pm']));]]></add>
138
+		</operation>
139
+		<operation><!-- expand the quote link= parameter -->
140
+			<search position="before"><![CDATA[(?:topic|threadid)=[\dmsg#\./]{1,40}(?:;start=[\dmsg#\./]{1,40})?]]></search>
141
+			<add><![CDATA[|action=helpdesk;sa=ticket;ticket=[\dmsg#\./]{1,40}(?:;start=[\dmsg#\./]{1,40})?]]></add>
142
+		</operation>
143
+		<operation><!-- mess with the main menu -->
144
+			<search position="after"><![CDATA[	$context['menu_buttons'] = $menu_buttons;]]></search>
145
+			<add><![CDATA[	// SD specific options
146
+	if (!empty($modSettings['helpdesk_active']))
147
+	{
148
+		// Stuff we'll always do in SD if active
149
+		if (allowedTo(array('admin_forum', 'admin_helpdesk')))
150
+		{
151
+			$menu_buttons['admin']['sub_buttons']['helpdesk_admin'] = array(
152
+				'title' => $txt['shd_helpdesk'],
153
+				'href' => $scripturl . '?action=admin;area=helpdesk_info',
154
+				'show' => allowedTo(array('admin_forum', 'admin_helpdesk')),
155
+			);
156
+		}
157
+
158
+
159
+		// Stuff we'll only do if in standalone mode
160
+		if (!empty($modSettings['shd_helpdesk_only']))
161
+		{
162
+			$menu_buttons['home'] = array(
163
+				'title' => $modSettings['helpdesk_active'] && SMF != 'SSI' ? shd_get_active_tickets() : $txt['shd_helpdesk'],
164
+				'href' => $scripturl . '?action=helpdesk;sa=main',
165
+				'show' => $modSettings['helpdesk_active'] && (allowedTo('access_helpdesk') || allowedTo('admin_helpdesk')),
166
+				'sub_buttons' => array(
167
+				),
168
+				'active_button' => false,
169
+			);
170
+			unset($menu_buttons['helpdesk']);
171
+
172
+			// Disable help, search, calendar, moderation center
173
+			unset($menu_buttons['help']);
174
+
175
+			unset($menu_buttons['search']);
176
+			$context['allow_search'] = false;
177
+
178
+			unset($menu_buttons['calendar']);
179
+			$context['allow_calendar'] = false;
180
+
181
+			unset($menu_buttons['moderate']);
182
+			$context['allow_moderation_center'] = false;
183
+
184
+			// Disable PMs
185
+			if (!empty($modSettings['shd_disable_pm']))
186
+			{
187
+				$context['allow_pm'] = false;
188
+				unset($menu_buttons['pm']);
189
+				$context['user']['unread_messages'] = 0; // to disable it trying to add to the menu item
190
+			}
191
+
192
+			// Disable memberlist
193
+			if (!empty($modSettings['shd_disable_mlist']))
194
+			{
195
+				$context['allow_memberlist'] = false;
196
+				unset($menu_buttons['mlist']);
197
+			}
198
+		}
199
+	}
200
+
201
+]]></add>
202
+		</operation>
203
+	</file>
204
+
205
+	<file name="$sourcedir/ManageSettings.php"><!-- add the helpdesk to Core Features in admin -->
206
+		<operation>
207
+			<search position="after"><![CDATA[		// k = karma.]]></search>
208
+			<add><![CDATA[		// shd = Helpdesk - Simple(Help)Desk
209
+		'shd' => array(
210
+			'url' => 'action=admin;area=helpdesk_info',
211
+		),
212
+]]></add>
213
+		</operation>
214
+	</file>
215
+	
216
+	<file name="$sourcedir/Display.php"><!-- add topic to ticket menu button -->
217
+		<operation>
218
+			<search position="after"><![CDATA[// We don't want to show the poll icon in the topic class here, so pretend it's not one.]]></search>
219
+			<add><![CDATA[// Simpledesk: Figure out if they can move this topic to the Helpdesk.
220
+	$context['can_move_to_helpdesk'] = allowedTo('shd_topic_to_ticket') && empty($modSettings['shd_helpdesk_only']);
221
+]]></add>
222
+		</operation>
223
+	</file>
224
+
225
+	<file name="$sourcedir/ManagePermissions.php"><!-- add all the glorious permissions in an uber extensible way without screwing with Modifications.{language}.php (it's on EVERY page load, and every little helps) -->
226
+		<operation>
227
+			<search position="after"><![CDATA[	// We need to know what permissions we can't give to guests.]]></search>
228
+			<add><![CDATA[	// Load the helpdesk permissions; in a painless manner that doesn't interfere with anything else :)
229
+	if ($modSettings['helpdesk_active'])
230
+	{
231
+		shd_load_language('SimpleDeskPermissions');
232
+		$permissionList['membergroup'] += array(
233
+			'access_helpdesk' => array(false, 'helpdesk', 'use_helpdesk'),
234
+			'shd_view_ticket' => array(true, 'helpdesk', 'use_helpdesk'),
235
+			'shd_view_ticket_private' => array(true, 'helpdesk', 'use_helpdesk'),
236
+			'shd_alter_urgency' => array(true, 'helpdesk', 'use_helpdesk'),
237
+			'shd_alter_urgency_higher' => array(true, 'helpdesk', 'use_helpdesk'),
238
+			'shd_new_ticket' => array(false, 'helpdesk', 'use_helpdesk'),
239
+			'shd_edit_ticket' => array(true, 'helpdesk', 'use_helpdesk'),
240
+			'shd_reply_ticket' => array(true, 'helpdesk', 'use_helpdesk'),
241
+			'shd_edit_reply' => array(true, 'helpdesk', 'use_helpdesk'),
242
+			'shd_post_attachment' => array(false, 'helpdesk', 'use_helpdesk'),
243
+			'shd_resolve_ticket' => array(true, 'helpdesk', 'use_helpdesk'),
244
+
245
+			'shd_staff' => array(false, 'shd_staff', 'shd_staff'),
246
+			'shd_alter_privacy' => array(true, 'shd_staff', 'shd_staff'),
247
+			'shd_assign_ticket' => array(true, 'shd_staff', 'shd_staff'),
248
+			'shd_delete_ticket' => array(true, 'shd_staff', 'shd_staff'),
249
+			'shd_delete_reply' => array(true, 'shd_staff', 'shd_staff'),
250
+			'shd_restore_ticket' => array(true, 'shd_staff', 'shd_staff'),
251
+			'shd_restore_reply' => array(true, 'shd_staff', 'shd_staff'),
252
+			'shd_ticket_to_topic' => array(false, 'shd_staff', 'shd_staff'),	
253
+			'shd_topic_to_ticket' => array(false, 'shd_staff', 'shd_staff'),			
254
+
255
+			'admin_helpdesk' => array(false, 'shd_admin', 'shd_admin'),
256
+			'shd_delete_recycling' => array(false, 'shd_admin', 'shd_admin'),
257
+			'shd_access_recyclebin' => array(false, 'shd_admin', 'shd_admin'),
258
+		);
259
+
260
+		$leftPermissionGroups[] = 'helpdesk';
261
+
262
+		// Also, are we in SHD only mode? If so... hoo boy, we gots some purging to do.
263
+		if (!empty($modSettings['shd_helpdesk_only']))
264
+		{
265
+			$perms_disable = array(
266
+				'membergroup' => array(
267
+					'view_stats',
268
+					'who_view',
269
+					'search_posts',
270
+					'karma_edit',
271
+					'calendar_view',
272
+					'calendar_post',
273
+					'calendar_edit',
274
+					'manage_boards',
275
+					'manage_attachments',
276
+					'manage_smileys',
277
+					'edit_news',
278
+					'access_mod_center',
279
+					'moderate_forum',
280
+					'send_mail',
281
+					'issue_warning',
282
+					'shd_ticket_to_topic', // yes, even our own, makes sense to reuse what we have here
283
+					'shd_topic_to_ticket',
284
+				),
285
+				'board' => array(
286
+					'moderate_board',
287
+					'approve_posts',
288
+					'post_new',
289
+					'post_unapproved_topics',
290
+					'post_unapproved_replies',
291
+					'post_reply',
292
+					'merge_any',
293
+					'split_any',
294
+					'send_topic',
295
+					'make_sticky',
296
+					'move',
297
+					'lock',
298
+					'remove',
299
+					'modify_replies',
300
+					'delete_replies',
301
+					'announce_topic',
302
+					'delete',
303
+					'modify',
304
+					'report_any',
305
+					'poll_view',
306
+					'poll_vote',
307
+					'poll_post',
308
+					'poll_add',
309
+					'poll_edit',
310
+					'poll_lock',
311
+					'poll_remove',
312
+					'mark_any_notify',
313
+					'mark_notify',
314
+					'view_attachments',
315
+					'post_unapproved_attachments',
316
+					'post_attachment',
317
+				),
318
+			);
319
+			// that's the generic stuff, now for specific options
320
+			if (!empty($modSettings['shd_disable_pm']))
321
+			{
322
+				$perms_disable['membergroup'][] = 'pm_read';
323
+				$perms_disable['membergroup'][] = 'pm_send';
324
+			}
325
+
326
+			foreach ($perms_disable as $group => $perm_list)
327
+				foreach ($perm_list as $perm)
328
+					if (isset($permissionList[$group][$perm]))
329
+						unset($permissionList[$group][$perm]);
330
+		}
331
+	}
332
+
333
+]]></add>
334
+		</operation>
335
+	</file>
336
+
337
+	<file name="$sourcedir/Help.php">
338
+		<operation><!-- load the permissions help strings -->
339
+			<search position="replace"><![CDATA[		loadLanguage('ManagePermissions');]]></search>
340
+			<add><![CDATA[	{
341
+		loadLanguage('ManagePermissions');
342
+		shd_load_language('SimpleDeskPermissions');
343
+	}]]></add>
344
+		</operation>
345
+		<operation><!-- load the admin help strings -->
346
+			<search position="replace"><![CDATA[	loadLanguage('Help');]]></search>
347
+			<add><![CDATA[	loadLanguage('Help');
348
+	shd_load_language('SimpleDeskAdmin');]]></add>
349
+		</operation>
350
+	</file>
351
+
352
+	<file name="$sourcedir/ManageBoards.php"><!-- load the permissions language file if we're managing boards -->
353
+		<operation>
354
+			<search position="before"><![CDATA[loadLanguage('ManagePermissions');]]></search>
355
+			<add><![CDATA[
356
+	shd_load_language('SimpleDeskPermissions');]]></add>
357
+		</operation>
358
+	</file>
359
+
360
+	<file name="$sourcedir/ManagePosts.php"><!-- at the same time we modify the post settings, make sure we check and update SD's if necessary -->
361
+		<operation>
362
+			<search position="before"><![CDATA[$smcFunc['db_change_column']('{db_prefix}messages', 'body', array('type' => 'mediumtext'));]]></search>
363
+			<add><![CDATA[
364
+					$smcFunc['db_change_column']('{db_prefix}helpdesk_ticket_replies', 'body', array('type' => 'mediumtext'));]]></add>
365
+		</operation>
366
+		<operation>
367
+			<search position="before"><![CDATA[$smcFunc['db_change_column']('{db_prefix}messages', 'body', array('type' => 'text'));]]></search>
368
+			<add><![CDATA[
369
+					$smcFunc['db_change_column']('{db_prefix}helpdesk_ticket_replies', 'body', array('type' => 'text'));]]></add>
370
+		</operation>
371
+	</file>
372
+
373
+	<file name="$sourcedir/Profile-View.php"><!-- load the permissions language file if we're looking at profile/permissions -->
374
+		<operation>
375
+			<search position="after"><![CDATA[	loadLanguage('ManagePermissions');]]></search>
376
+			<add><![CDATA[	shd_load_language('SimpleDeskPermissions');
377
+]]></add>
378
+		</operation>
379
+	</file>
380
+
381
+	<file name="$sourcedir/Reports.php"><!-- load the permissions language file if we're getting a report -->
382
+		<operation>
383
+			<search position="before"><![CDATA[loadLanguage('ManagePermissions');]]></search>
384
+			<add><![CDATA[
385
+	shd_load_language('SimpleDeskPermissions');]]></add>
386
+		</operation>
387
+	</file>
388
+
389
+	<file name="$sourcedir/Who.php"><!-- load the who's online strings... we don't use it much now, but maybe later... -->
390
+		<operation>
391
+			<search position="before"><![CDATA[loadLanguage('Who');]]></search>
392
+			<add><![CDATA[
393
+	shd_load_language('SimpleDeskWho');]]></add>
394
+		</operation>
395
+	</file>
396
+
397
+	<file name="$sourcedir/Errors.php"><!-- if an error occurs in an SD file, log it appropriately with the SD errortype -->
398
+		<operation>
399
+			<search position="after"><![CDATA[	// Make sure the category that was specified is a valid one]]></search>
400
+			<add><![CDATA[	// Is this a SimpleDesk error?
401
+	$known_error_types[] = 'simpledesk';
402
+	if (strpos($file, 'SimpleDesk') !== false || strpos($error_message, 'shd_') !== false)
403
+		$error_type = 'simpledesk';
404
+
405
+]]></add>
406
+		</operation>
407
+	</file>
408
+
409
+	<file name="$boarddir/index.php"><!-- set up the relevant action stuff -->
410
+		<operation><!-- add/remove action without having to touch the master array - cleaner! -->
411
+			<search position="after"><![CDATA[	// Get the function and file to include - if it's not there, do the board index.]]></search>
412
+			<add><![CDATA[	// Deal with SimpleDesk. If we're enabling HD only mode, rebuild everything, otherwise just add it to the array.
413
+	$actionArray['helpdesk'] = array('SimpleDesk.php', 'shd_main');
414
+	if (!empty($modSettings['shd_helpdesk_only']) && $modSettings['helpdesk_active'])
415
+	{
416
+		// Firstly, remove all the standard actions we neither want nor need.
417
+		// Note we did this to prevent breakage of other mods that may be installed, e.g. gallery or portal or something.
418
+		$unwanted_actions = array('announce', 'attachapprove', 'buddy', 'calendar', 'clock', 'collapse', 'deletemsg', 'display', 'editpoll', 'editpoll2',
419
+			'emailuser', 'groups', 'lock', 'lockvoting', 'markasread', 'mergetopics', 'moderate', 'modifycat', 'modifykarma', 'movetopic', 'movetopic2',
420
+			'notify', 'notifyboard', 'post', 'post2', 'printpage', 'quotefast', 'quickmod', 'quickmod2', 'recent', 'reminder', 'removepoll', 'removetopic2',
421
+			'reporttm', 'restoretopic', 'search', 'search2', 'sendtopic', 'smstats', 'splittopics', 'stats', 'sticky', 'trackip', 'about:mozilla', 'about:unknown',
422
+			'unread', 'unreadreplies', 'vote', 'viewquery', 'who', '.xml', 'xmlhttp');
423
+
424
+		// that's the generic stuff, now for specific options
425
+		if (!empty($modSettings['shd_disable_pm']))
426
+			$unwanted_actions[] = 'pm';
427
+
428
+		if (!empty($modSettings['shd_disable_mlist']))
429
+			$unwanted_actions[] = 'mlist';
430
+
431
+		foreach ($unwanted_actions as $unwanted)
432
+			if (isset($actionArray[$unwanted]))
433
+				unset($actionArray[$unwanted]);
434
+
435
+		// Secondly, rewrite the defaults to point to helpdesk, for unknown actions. I'm doing this rather than munging the main code - easier to unbreak stuff
436
+		if (empty($actionArray[$_GET['action']]))
437
+			$_GET['action'] = 'helpdesk';
438
+	}
439
+]]></add>
440
+		</operation>
441
+		<operation><!-- add a master override if we haven't declared an action -->
442
+			<search position="after"><![CDATA[		// Action and board are both empty... BoardIndex!]]></search>
443
+			<add><![CDATA[		// If we're in HD-only mode, let's run with that!
444
+		if (!empty($modSettings['shd_helpdesk_only']) && $modSettings['helpdesk_active'])
445
+		{
446
+			require_once($sourcedir . '/SimpleDesk.php');
447
+			return 'shd_main';
448
+		}
449
+]]></add>
450
+		</operation>
451
+		<operation><!-- add a catcher in case it's a function we don't have -->
452
+			<search position="after"><![CDATA[		// Fall through to the board index then...
453
+		require_once($sourcedir . '/BoardIndex.php');
454
+		return 'BoardIndex';]]></search>
455
+			<add><![CDATA[		// If we're in HD-only mode, we can't let them go anywhere useless
456
+		if (!empty($modSettings['shd_helpdesk_only']) && $modSettings['helpdesk_active'])
457
+		{
458
+			require_once($sourcedir . '/SimpleDesk.php');
459
+			return 'shd_main';
460
+		}
461
+
462
+]]></add>
463
+		</operation>
464
+		<operation><!-- bootstrap the SimpleDesk functions a little -->
465
+			<search position="before"><![CDATA[require_once($sourcedir . '/Security.php');]]></search>
466
+			<add><![CDATA[
467
+require_once($sourcedir . '/Subs-SimpleDesk.php');]]></add>		
468
+		</operation>
469
+	</file>
470
+
471
+	<!-- theme edits boohoo -->
472
+	<file name="$themedir/Display.template.php"><!-- add topic to ticket menu button -->
473
+		<operation>
474
+			<search position="after"><![CDATA[// Show the page index... "Pages: [1]".]]></search>
475
+			<add><![CDATA[	// SimpleDesk: Add topic -> ticket button
476
+	$normal_buttons = array_merge($normal_buttons,array('topictoticket' => array('test' => 'can_move_to_helpdesk', 'text' => 'shd_move_topic_to_ticket', 'lang' => true, 'url' => $scripturl . '?action=helpdesk;sa=topictoticket;topic='.$context['current_topic'].';' . $context['session_var'] . '=' . $context['session_id'])));
477
+]]></add>
478
+		</operation>
479
+	</file>
480
+
481
+	<!-- remove the unread/unreadreplies links in SD only mode -->
482
+	<!-- unfortunately, these pretty much have to be atomic edits :( -->
483
+	<file name="$themedir/index.template.php">
484
+		<operation error="ignore">
485
+			<search position="replace"><![CDATA[
486
+					<li><a href="', $scripturl, '?action=unread">', $txt['unread_since_visit'], '</a></li>
487
+					<li><a href="', $scripturl, '?action=unreadreplies">', $txt['show_unread_replies'], '</a></li>';]]></search>
488
+			<add><![CDATA[
489
+					', (!empty($modSettings['helpdesk_active']) && !empty($modSettings['shd_helpdesk_only'])) ? '' : ('<li><a href="' . $scripturl . '?action=unread">' . $txt['unread_since_visit'] . '</a></li>
490
+					<li><a href="' . $scripturl . '?action=unreadreplies">' . $txt['show_unread_replies'] . '</a></li>');]]></add>
491
+		</operation>
492
+	</file>
493
+
494
+	<file name="$themes_dir/core/index.template.php" error="skip">
495
+		<operation error="ignore">
496
+			<search position="replace"><![CDATA[
497
+					<li><a href="', $scripturl, '?action=unread">', $txt['unread_since_visit'], '</a></li>
498
+					<li><a href="', $scripturl, '?action=unreadreplies">', $txt['show_unread_replies'], '</a></li>';]]></search>
499
+			<add><![CDATA[
500
+					', (!empty($modSettings['helpdesk_active']) && !empty($modSettings['shd_helpdesk_only'])) ? '' : ('<li><a href="' . $scripturl . '?action=unread">' . $txt['unread_since_visit'] . '</a></li>
501
+					<li><a href="' . $scripturl . '?action=unreadreplies">' . $txt['show_unread_replies'] . '</a></li>');]]></add>
502
+		</operation>
503
+	</file>
504
+
505
+</modification>
0 506
\ No newline at end of file
... ...
@@ -0,0 +1,508 @@
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: 1.0 Felidae                         #
17
+# File Info: install.php / 1.0 Felidae                    #
18
+###########################################################
19
+
20
+/**
21
+ *	This script prepares the database for all the tables and other database changes that SimpleDesk requires.
22
+ *
23
+ *	NOTE: This script is meant to run using the <samp><database></database></samp> elements of the package-info.xml file. This is so
24
+ *	that admins have the choice to uninstall any database data installed with the mod. Also, since using the <samp><database></samp>
25
+ *	elements automatically calls on db_extend('packages'), we will only be calling that if we are running this script standalone.
26
+ *
27
+ *	@package installer
28
+ *	@since 1.0
29
+ */
30
+
31
+/**
32
+ *	Before attempting to execute, this file attempts to load SSI.php to enable access to the database functions.
33
+*/
34
+
35
+// If we have found SSI.php and we are outside of SMF, then we are running standalone.
36
+if (file_exists(dirname(__FILE__) . '/SSI.php') && !defined('SMF'))
37
+	require_once(dirname(__FILE__) . '/SSI.php');
38
+elseif (!defined('SMF')) // If we are outside SMF and can't find SSI.php, then throw an error
39
+	die('<b>Error:</b> Cannot install - please verify you put this file in the same place as SMF\'s SSI.php.');
40
+
41
+if (SMF == 'SSI')
42
+	db_extend('packages');
43
+
44
+global $modSettings, $smcFunc;
45
+
46
+// For our BBC settings, we first fetch a list off all BBC tags there are.
47
+$bbc_tags = array();
48
+foreach (parse_bbc(false) AS $tag)
49
+	$bbc_tags[] = $tag['tag'];
50
+
51
+// Here we will update the $modSettings variables.
52
+$mod_settings = array(
53
+	'shd_attachments_mode' => 'ticket',
54
+	'shd_staff_badge' => 'nobadge',
55
+	'shd_ticketnav_style' => 'sd',
56
+	'shd_enabled_bbc' => implode(',', $bbc_tags),	// By default, all available tags are enabled.
57
+	'shd_privacy_display' => 'smart',
58
+);
59
+
60
+// Now, we move on to adding new tables to the database.
61
+$tables = array();
62
+$tables[] = array(
63
+	'table_name' => '{db_prefix}helpdesk_tickets',
64
+	'columns' => array(
65
+		array(
66
+			'name' => 'id_ticket',
67
+			'auto' => true,
68
+			'type' => 'mediumint',
69
+			'size' => 8,
70
+			'null' => false,
71
+			'unsigned' => true,
72
+		),
73
+		array(
74
+			'name' => 'id_first_msg',
75
+			'auto' => false,
76
+			'type' => 'int',
77
+			'size' => 10,
78
+			'null' => false,
79
+			'unsigned' => true,
80
+			'default' => 0,
81
+		),
82
+		array(
83
+			'name' => 'id_member_started',
84
+			'auto' => false,
85
+			'type' => 'mediumint',
86
+			'size' => 8,
87
+			'null' => false,
88
+			'unsigned' => true,
89
+			'default' => 0,
90
+		),
91
+		array(
92
+			'name' => 'id_last_msg',
93
+			'auto' => false,
94
+			'type' => 'int',
95
+			'size' => 10,
96
+			'null' => false,
97
+			'unsigned' => true,
98
+			'default' => 0,
99
+		),
100
+		array(
101
+			'name' => 'id_member_updated',
102
+			'auto' => false,
103
+			'type' => 'mediumint',
104
+			'size' => 8,
105
+			'null' => false,
106
+			'unsigned' => true,
107
+			'default' => 0,
108
+		),
109
+		array(
110
+			'name' => 'id_member_assigned',
111
+			'auto' => false,
112
+			'type' => 'mediumint',
113
+			'size' => 8,
114
+			'null' => false,
115
+			'unsigned' => true,
116
+			'default' => 0,
117
+		),
118
+		array(
119
+			'name' => 'num_replies',
120
+			'auto' => false,
121
+			'type' => 'int',
122
+			'size' => 10,
123
+			'null' => false,
124
+			'unsigned' => true,
125
+			'default' => 0,
126
+		),
127
+		array(
128
+			'name' => 'deleted_replies',
129
+			'auto' => false,
130
+			'type' => 'int',
131
+			'size' => 10,
132
+			'null' => false,
133
+			'unsigned' => true,
134
+			'default' => 0,
135
+		),
136
+		array(
137
+			'name' => 'subject',
138
+			'auto' => false,
139
+			'type' => 'varchar',
140
+			'size' => 100,
141
+			'null' => false,
142
+		),
143
+		array(
144
+			'name' => 'urgency',
145
+			'auto' => false,
146
+			'type' => 'tinyint',
147
+			'size' => 3,
148
+			'null' => false,
149
+			'unsigned' => true,
150
+			'default' => 0,
151
+		),
152
+		array(
153
+			'name' => 'status',
154
+			'auto' => false,
155
+			'type' => 'tinyint',
156
+			'size' => 3,
157
+			'null' => false,
158
+			'unsigned' => true,
159
+			'default' => 0,
160
+		),
161
+		array(
162
+			'name' => 'private',
163
+			'auto' => false,
164
+			'type' => 'tinyint',
165
+			'size' => 3,
166
+			'null' => false,
167
+			'unsigned' => true,
168
+			'default' => 0,
169
+		),
170
+		array(
171
+			'name' => 'withdeleted',
172
+			'auto' => false,
173
+			'type' => 'tinyint',
174
+			'size' => 3,
175
+			'null' => false,
176
+			'unsigned' => true,
177
+			'default' => 0,
178
+		),
179
+	),
180
+	'indexes' => array(
181
+		array(
182
+			'columns' => array('id_ticket'),
183
+			'type' => 'primary',
184
+		),
185
+		array(
186
+			'columns' => array('status', 'id_member_assigned'),
187
+			'type' => 'index',
188
+		),
189
+		array(
190
+			'columns' => array('id_member_started', 'private'),
191
+			'type' => 'index',
192
+		),
193
+		array(
194
+			'columns' => array('status', 'withdeleted', 'deleted_replies'),
195
+		),
196
+	),
197
+	'if_exists' => 'update',
198
+	'error' => 'fatal',
199
+	'parameters' => array(),
200
+);
201
+$tables[] = array(
202
+	'table_name' => '{db_prefix}helpdesk_ticket_replies',
203
+	'columns' => array(
204
+		array(
205
+			'name' => 'id_msg',
206
+			'auto' => true,
207
+			'type' => 'int',
208
+			'size' => 10,
209
+			'null' => false,
210
+			'unsigned' => true,
211
+		),
212
+		array(
213
+			'name' => 'id_ticket',
214
+			'auto' => false,
215
+			'type' => 'mediumint',
216
+			'size' => 8,
217
+			'null' => false,
218
+			'unsigned' => true,
219
+		),
220
+		array(
221
+			'name' => 'body',
222
+			'auto' => false,
223
+			'type' => $modSettings['max_messageLength'] > 65535 ? 'text' : 'mediumtext',
224
+			'null' => false,
225
+		),
226
+		array(
227
+			'name' => 'id_member',
228
+			'auto' => false,
229
+			'type' => 'mediumint',
230
+			'size' => 8,
231
+			'default' => 0,
232
+			'null' => false,
233
+			'unsigned' => true,
234
+		),
235
+		array(
236
+			'name' => 'poster_time',
237
+			'auto' => false,
238
+			'type' => 'int',
239
+			'size' => 10,
240
+			'default' => 0,
241
+			'null' => false,
242
+			'unsigned' => true,
243
+		),
244
+		array(
245
+			'name' => 'poster_name',
246
+			'auto' => false,
247
+			'type' => 'varchar',
248
+			'size' => 255,
249
+			'null' => false,
250
+		),
251
+		array(
252
+			'name' => 'poster_email',
253
+			'auto' => false,
254
+			'type' => 'varchar',
255
+			'size' => 255,
256
+			'null' => false,
257
+		),
258
+		array(
259
+			'name' => 'poster_ip',
260
+			'auto' => false,
261
+			'type' => 'varchar',
262
+			'size' => 255,
263
+			'null' => false,
264
+		),
265
+		array(
266
+			'name' => 'modified_time',
267
+			'auto' => false,
268
+			'type' => 'int',
269
+			'size' => 10,
270
+			'default' => 0,
271
+			'null' => false,
272
+			'unsigned' => true,
273
+		),
274
+		array(
275
+			'name' => 'modified_member',
276
+			'auto' => false,
277
+			'type' => 'mediumint',
278
+			'size' => 8,
279
+			'default' => 0,
280
+			'null' => false,
281
+			'unsigned' => true,
282
+		),
283
+		array(
284
+			'name' => 'modified_name',
285
+			'auto' => false,
286
+			'type' => 'varchar',
287
+			'size' => 255,
288
+			'null' => false,
289
+		),
290
+		array(
291
+			'name' => 'smileys_enabled',
292
+			'auto' => false,
293
+			'type' => 'tinyint',
294
+			'size' => 3,
295
+			'default' => 0,
296
+			'null' => false,
297
+		),
298
+		array(
299
+			'name' => 'message_status',
300
+			'auto' => false,
301
+			'type' => 'tinyint',
302
+			'size' => 3,
303
+			'default' => 0,
304
+			'null' => false,
305
+		),
306
+	),
307
+	'indexes' => array(
308
+		array(
309
+			'columns' => array('id_msg'),
310
+			'type' => 'primary',
311
+		),
312
+		array(
313
+			'columns' => array('id_ticket', 'id_msg', 'message_status'),
314
+			'type' => 'index',
315
+		),
316
+	),
317
+	'if_exists' => 'update',
318
+	'error' => 'fatal',
319
+	'parameters' => array(),
320
+);
321
+$tables[] = array(
322
+	'table_name' => '{db_prefix}helpdesk_log_action',
323
+	'columns' => array(
324
+		array(
325
+			'name' => 'id_action',
326
+			'auto' => true,
327
+			'type' => 'int',
328
+			'size' => 10,
329
+			'null' => false,
330
+			'unsigned' => true,
331
+		),
332
+		array(
333
+			'name' => 'log_time', // when this happened
334
+			'auto' => false,
335
+			'type' => 'int',
336
+			'size' => 10,
337
+			'default' => 0,
338
+			'null' => false,
339
+			'unsigned' => true,
340
+		),
341
+		array(
342
+			'name' => 'id_member', // person making the event
343
+			'auto' => false,
344
+			'type' => 'mediumint',
345
+			'size' => 8,
346
+			'default' => 0,
347
+			'null' => false,
348
+			'unsigned' => true,
349
+		),
350
+		array(
351
+			'name' => 'ip', // member's IP
352
+			'auto' => false,
353
+			'type' => 'char',
354
+			'size' => 16,
355
+			'null' => false,
356
+		),
357
+		array(
358
+			'name' => 'action', // defines the message to use
359
+			'auto' => false,
360
+			'type' => 'varchar',
361
+			'size' => 30,
362
+			'null' => false,
363
+		),
364
+		array(
365
+			'name' => 'id_ticket', // ticket it applies to
366
+			'auto' => false,
367
+			'type' => 'mediumint',
368
+			'size' => 8,
369
+			'default' => 0,
370
+			'null' => false,
371
+		),
372
+		array(
373
+			'name' => 'id_msg', // msg it applies to
374
+			'auto' => false,
375
+			'type' => 'int',
376
+			'size' => 10,
377
+			'default' => 0,
378
+			'null' => false,
379
+		),
380
+		array(
381
+			'name' => 'extra', // serialised array of params for log message
382
+			'auto' => false,
383
+			'type' => 'mediumtext',
384
+			'null' => false,
385
+		),
386
+	),
387
+	'indexes' => array(
388
+		array(
389
+			'columns' => array('id_action'),
390
+			'type' => 'primary',
391
+		),
392
+	),
393
+	'if_exists' => 'update',
394
+	'error' => 'fatal',
395
+	'parameters' => array(),
396
+);
397
+$tables[] = array(
398
+	'table_name' => '{db_prefix}helpdesk_log_read',
399
+	'columns' => array(
400
+		array(
401
+			'name' => 'id_ticket',
402
+			'auto' => false,
403
+			'default' => 0,
404
+			'type' => 'mediumint',
405
+			'size' => 8,
406
+			'null' => false,
407
+			'unsigned' => true,
408
+		),
409
+		array(
410
+			'name' => 'id_member',
411
+			'auto' => false,
412
+			'default' => 0,
413
+			'type' => 'mediumint',
414
+			'size' => 8,
415
+			'null' => false,
416
+			'unsigned' => true,
417
+		),
418
+		array(
419
+			'name' => 'id_msg',
420
+			'auto' => false,
421
+			'default' => 0,
422
+			'type' => 'int',
423
+			'size' => 10,
424
+			'null' => false,
425
+			'unsigned' => true,
426
+		),
427
+	),
428
+	'indexes' => array(
429
+		array(
430
+			'columns' => array('id_ticket', 'id_member'),
431
+			'type' => 'primary',
432
+		),
433
+	),
434
+	'if_exists' => 'update',
435
+	'error' => 'fatal',
436
+	'parameters' => array(),
437
+);
438
+$tables[] = array(
439
+	'table_name' => '{db_prefix}helpdesk_attachments',
440
+	'columns' => array(
441
+		array(
442
+			'name' => 'id_attach',
443
+			'auto' => false,
444
+			'default' => 0,
445
+			'type' => 'int',
446
+			'size' => 10,
447
+			'null' => false,
448
+			'unsigned' => true,
449
+		),
450
+		array(
451
+			'name' => 'id_ticket',
452
+			'auto' => false,
453
+			'default' => 0,
454
+			'type' => 'mediumint',
455
+			'size' => 8,
456
+			'null' => false,
457
+			'unsigned' => true,
458
+		),
459
+		array(
460
+			'name' => 'id_msg',
461
+			'auto' => false,
462
+			'default' => 0,
463
+			'type' => 'int',
464
+			'size' => 10,
465
+			'null' => false,
466
+			'unsigned' => true,
467
+		),
468
+	),
469
+	'indexes' => array(
470
+		array(
471
+			'columns' => array('id_attach'),
472
+			'type' => 'primary',
473
+		),
474
+	),
475
+	'if_exists' => 'update',
476
+	'error' => 'fatal',
477
+	'parameters' => array(),
478
+);
479
+
480
+// Oh joy, we've now made it to extra rows... (testing data)
481
+$rows = array();
482
+
483
+// Now we can add a new column to an existing table
484
+$columns = array();
485
+
486
+// Update mod settings if applicable
487
+foreach ($mod_settings as $new_setting => $new_value)
488
+{
489
+	if (empty($modSettings[$new_setting]))
490
+		updateSettings(array($new_setting => $new_value));
491
+}
492
+
493
+// Create new tables, if any
494
+foreach ($tables as $table)
495
+	$smcFunc['db_create_table']($table['table_name'], $table['columns'], $table['indexes'], $table['parameters'], $table['if_exists'], $table['error']);
496
+
497
+// Create new rows, if any
498
+foreach ($rows as $row)
499
+	$smcFunc['db_insert']($row['method'], $row['table_name'], $row['columns'], $row['data'], $row['keys']);
500
+
501
+// Create new columns, if any
502
+foreach ($columns as $column)
503
+	$smcFunc['db_add_column']($column['table_name'], $column['column_info'], $column['parameters'], $column['if_exists'], $column['error']);
504
+
505
+// Are we done?
506
+if (SMF == 'SSI')
507
+	echo 'Database changes are complete!';
508
+?>
0 509
\ No newline at end of file
... ...
@@ -0,0 +1,282 @@
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: 1.0 Felidae                             #
17
+# File Info: SimpleDesk.english.php / 1.0 Felidae             #
18
+###############################################################
19
+// Version: 1.0 Felidae; 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
+/**
24
+ *	This file contains all of the principle language strings used by SimpleDesk and are loaded on every page.
25
+ *
26
+ *	@package language
27
+ *	@todo Document the text groups in this file.
28
+ *	@since 1.0
29
+ */
30
+
31
+$txt['shd_helpdesk'] = 'Helpdesk';
32
+
33
+//! @name Admininstration panel strings
34
+//@{
35
+$txt['shd_admin_welcome'] = 'Welcome to the main helpdesk administration center!';
36
+$txt['shd_admin_title'] = 'Helpdesk Administration Center';
37
+$txt['shd_staff_list'] = 'Helpdesk staff';
38
+$txt['shd_update_available'] = 'New version available!';
39
+$txt['shd_update_message'] = 'A new version of SimpleDesk has been released. We recommened you to <a href="#" id="update-link">update to the latest version</a> in order to stay secure and enjoy all features our modification have to offer.
40
+<span style="display: none;" id="information-link-span"><br />For more information on what is new in this release, please visit <a href="#" id="information-link" target="_blank">our website</a>.</span><br />
41
+<strong>The SimpleDesk Team</strong>';
42
+//@}
43
+
44
+//! @name Urgency levels, ties to helpdesk_tickets.urgency
45
+//@{
46
+$txt['shd_urgency_0'] = 'Low';
47
+$txt['shd_urgency_1'] = 'Medium';
48
+$txt['shd_urgency_2'] = 'High';
49
+$txt['shd_urgency_3'] = 'Very High';
50
+$txt['shd_urgency_4'] = 'Severe';
51
+$txt['shd_urgency_5'] = 'Critical';
52
+$txt['shd_urgency_increase'] = 'Increase';
53
+$txt['shd_urgency_decrease'] = 'Decrease';
54
+//@}
55
+
56
+//! @name Status, ties to helpdesk_tickets.status
57
+//@{
58
+$txt['shd_status_0'] = 'New';
59
+$txt['shd_status_1'] = 'Pending Staff Comment';
60
+$txt['shd_status_2'] = 'Pending User Comment';
61
+$txt['shd_status_3'] = 'Resolved/Closed';
62
+$txt['shd_status_4'] = 'Referred to Supervisor';
63
+$txt['shd_status_5'] = 'Escalated - Urgent';
64
+$txt['shd_status_6'] = 'Deleted';
65
+//@}
66
+
67
+//! @name Headings, for the helpdesk listing pages
68
+//@{
69
+$txt['shd_status_0_heading'] = 'New Tickets';
70
+$txt['shd_status_1_heading'] = 'Tickets Awaiting Staff Response';
71
+$txt['shd_status_2_heading'] = 'Tickets Awaiting User Response';
72
+$txt['shd_status_3_heading'] = 'Closed Tickets';
73
+$txt['shd_status_4_heading'] = 'Tickets Referred to Supervisor';
74
+$txt['shd_status_5_heading'] = 'Urgent Tickets';
75
+$txt['shd_status_6_heading'] = 'Recycled Tickets';
76
+$txt['shd_status_assigned_heading'] = 'Assigned to Me';
77
+$txt['shd_status_withdeleted_heading'] = 'Tickets with Deleted Replies';
78
+//@}
79
+
80
+$txt['shd_tickets_open'] = 'Open Tickets';
81
+$txt['shd_tickets_closed'] = 'Closed Tickets';
82
+$txt['shd_tickets_recycled'] = 'Recycled Tickets';
83
+
84
+$txt['shd_assigned'] = 'Assigned';
85
+$txt['shd_unassigned'] = 'Unassigned';
86
+
87
+$txt['shd_read_ticket'] = 'Read Ticket';
88
+$txt['shd_unread_ticket'] = 'Unread Ticket';
89
+
90
+$txt['shd_owned'] = 'Owned Ticket'; // aka Assigned to Me
91
+
92
+$txt['shd_count_ticket_1'] = 'ticket';
93
+$txt['shd_count_tickets'] = 'tickets';
94
+
95
+// Errors
96
+$txt['cannot_access_helpdesk'] = 'You are not permitted to access the helpdesk.';
97
+$txt['shd_no_ticket'] = 'The ticket you have requested does not appear to exist.';
98
+$txt['shd_no_topic'] = 'The topic you have requested does not appaear to exist.';
99
+$txt['shd_ticket_no_perms'] = 'You do not have permission to view that ticket.';
100
+$txt['shd_error_no_tickets'] = 'No tickets were found.';
101
+$txt['shd_inactive'] = 'The helpdesk is currently deactivated.';
102
+$txt['shd_cannot_assign'] = 'You are not permitted to assign tickets.';
103
+$txt['shd_cannot_assign_other'] = 'This ticket is already assigned to another user. You cannot reassign it to yourself - please contact the administrator.';
104
+$txt['shd_no_staff_assign'] = 'There are no staff configured; it is not possible to assign a ticket. Please contact your administrator.';
105
+$txt['shd_assigned_not_permitted'] = 'The user you have requested to assign this ticket to does not have sufficient permissions to see it.';
106
+$txt['shd_cannot_resolve'] = 'You do not have permission to mark this ticket as resolved.';
107
+$txt['shd_cannot_change_privacy'] = 'You do not have permission to alter the privacy on this ticket.';
108
+$txt['shd_cannot_change_urgency'] = 'You do not have permission to alter the urgency on this ticket.';
109
+$txt['shd_ajax_problem'] = 'There was a problem attempting to load the page. Would you like to try again?';
110
+$txt['shd_cannot_move_ticket'] = 'You do not have permission to move this ticket to a topic.';
111
+$txt['shd_cannot_move_topic'] = 'You do not have permission to move this topic to a ticket.';
112
+$txt['shd_moveticket_noboards'] = 'There are no boards to move this ticket to!';
113
+$txt['shd_move_no_pm'] = 'You must enter a reason for moving the ticket to send to the ticket owner, or uncheck the option to \'send a PM to the ticket owner\'.';
114
+$txt['shd_move_no_pm_topic'] = 'You must enter a reason for moving the topic to send to the topic starter, or uncheck the option to \'send a PM to the topic starter\'.';
115
+$txt['shd_move_topic_not_created'] = 'Failed to move the ticket to the board. Please try again.';
116
+$txt['shd_move_ticket_not_created'] = 'Failed to move the topic to the helpdesk. Please try again.';
117
+$txt['shd_no_replies'] = 'This ticket does not have any replies yet.';
118
+$txt['cannot_shd_new_ticket'] = 'You do not have permission to create a new ticket.';
119
+$txt['cannot_shd_edit_ticket'] = 'You do not have permission to edit this ticket.';
120
+$txt['shd_cannot_reply_any'] = 'You do not have permission to reply to any tickets.';
121
+$txt['shd_cannot_reply_any_but_own'] = 'You do not have permission to reply to any tickets other than your own.';
122
+$txt['shd_cannot_edit_reply_any'] = 'You do not have permission to edit any replies.';
123
+$txt['shd_cannot_edit_reply_any_but_own'] = 'You do not have permission to edit replies to any tickets other than your own replies.';
124
+$txt['shd_cannot_edit_closed'] = 'You cannot edit resolved tickets; you need to mark it unresolved first.';
125
+$txt['shd_cannot_edit_deleted'] = 'You cannot edit tickets in the recycle bin. They will need to be restored first.';
126
+$txt['shd_cannot_reply_closed'] = 'You cannot reply to resolved tickets; you need to mark them unresolved first.';
127
+$txt['shd_cannot_reply_deleted'] = 'You cannot reply to tickets in the recycle bin. They will need to be restored first.';
128
+$txt['shd_cannot_delete_ticket'] = 'You are not permitted to delete this ticket.';
129
+$txt['shd_cannot_delete_reply'] = 'You are not permitted to delete that reply.';
130
+$txt['shd_cannot_restore_ticket'] = 'You are not permitted to restore this ticket from the recycle bin.';
131
+$txt['shd_cannot_restore_reply'] = 'You are not permitted to restore that reply from the recycle bin.';
132
+$txt['shd_cannot_view_resolved'] = 'You are not permitted to access resolved tickets.';
133
+$txt['cannot_shd_access_recyclebin'] = 'You cannot access the recycle bin.';
134
+$txt['shd_cannot_move_ticket_with_deleted'] = 'You cannot move this ticket to the forum; there are one or more deleted replies, which your current permissions do not permit access to.';
135
+$txt['shd_cannot_attach_ext'] = 'The type of file you have tried to attach ({ext}) is not allowed here. The allowed types of file are: {attach_exts}';
136
+
137
+// The main Helpdesk.
138
+$txt['shd_home'] = 'Home';
139
+$txt['shd_new_ticket'] = 'Post New Ticket';
140
+$txt['shd_welcome'] = 'Welcome, %s!';
141
+$txt['shd_go'] = 'Go!';
142
+$txt['shd_go_to_ticket'] = 'Go to ticket';
143
+
144
+$txt['shd_user_greeting'] = 'Here you can file new tickets for the site staff to action, and check on current tickets already underway.';
145
+$txt['shd_staff_greeting'] = 'Here are all the tickets that require attention.';
146
+$txt['shd_shd_greeting'] = 'This is the Helpdesk. Here you waste your time to help newbies. Enjoy! ;D';
147
+$txt['shd_closed_user_greeting'] = 'These are all the closed/resolved tickets you have posted to the helpdesk.';
148
+$txt['shd_closed_staff_greeting'] = 'These are all closed/resolved tickets submitted to the helpdesk.';
149
+
150
+// The main ticket view.
151
+$txt['shd_ticket_details'] = 'Ticket details';
152
+$txt['shd_ticket_updated'] = 'Updated';
153
+$txt['shd_ticket_id'] = 'Id';
154
+$txt['shd_ticket_name'] = 'Name';
155
+$txt['shd_ticket_user'] = 'User';
156
+$txt['shd_ticket_date'] = 'Posted';
157
+$txt['shd_ticket_urgency'] = 'Urgency';
158
+$txt['shd_ticket_assigned'] = 'Assigned';
159
+$txt['shd_ticket_assignedto'] = 'Assigned to';
160
+$txt['shd_ticket_started_by'] = 'Started by';
161
+$txt['shd_ticket_updated_by'] = 'Updated by';
162
+$txt['shd_ticket_status'] = 'Status';
163
+$txt['shd_ticket_num_replies'] = 'Replies';
164
+$txt['shd_ticket_replies'] = 'Replies';
165
+$txt['shd_ticket_staff'] = 'Staff member';
166
+$txt['shd_ticket_attachments'] = 'Attachments';
167
+$txt['shd_ticket_reply_number'] = 'Reply <strong>#%s</strong>'; // 'Reply #34'
168
+$txt['shd_ticket'] = 'Ticket';
169
+$txt['shd_reply_written'] = 'Reply written %s'; // 'Reply written Today at 04:12:29 pm',  'Reply written January 5 2009 04:12:29 pm'
170
+$txt['shd_never'] = 'Never';
171
+$txt['shd_linktree_tickets'] = 'Tickets';
172
+$txt['shd_ticket_privacy'] = 'Privacy';
173
+$txt['shd_ticket_notprivate'] = 'Not Private';
174
+$txt['shd_ticket_private'] = 'Private';
175
+$txt['shd_ticket_change'] = 'Change';
176
+$txt['shd_ticket_ip'] = 'IP address';
177
+$txt['shd_back_to_hd'] = 'Back to the helpdesk';
178
+
179
+$txt['shd_ticket_has_been_deleted'] = 'This ticket is currently in the recycle bin and cannot be altered without being returned to the helpdesk.';
180
+$txt['shd_ticket_replies_deleted'] = 'This ticket has had replies deleted from it previously.';
181
+$txt['shd_ticket_replies_deleted_view'] = 'These are displayed with a colored background. <a href="%1$s">View the ticket without deletions</a>.';
182
+$txt['shd_ticket_replies_deleted_link'] = 'Please <a href="%1$s">click here</a> to view them.';
183
+
184
+$txt['shd_ticket_notnew'] = 'You have already seen this';
185
+$txt['shd_ticket_new'] = 'New!';
186
+
187
+$txt['shd_linktree_move_ticket'] = 'Move ticket';
188
+$txt['shd_linktree_move_topic'] = 'Move topic to helpdesk';
189
+
190
+$txt['shd_cancel_ticket'] = 'Cancel and return to the ticket';
191
+$txt['shd_cancel_home'] = 'Cancel and return to the helpdesk home';
192
+$txt['shd_cancel_topic'] = 'Cancel and return to the topic';
193
+
194
+// Actions
195
+$txt['shd_ticket_reply'] = 'Reply to ticket';
196
+$txt['shd_ticket_quote'] = 'Reply with quote';
197
+$txt['shd_go_advanced'] = 'Go advanced!';
198
+$txt['shd_ticket_edit_reply'] = 'Edit reply';
199
+$txt['shd_ticket_quote_short'] = 'Quote';
200
+$txt['shd_ticket_markunread'] = 'Mark as unread';
201
+$txt['shd_ticket_reply_short'] = 'Reply';
202
+$txt['shd_ticket_edit'] = 'Edit';
203
+$txt['shd_ticket_resolved'] = 'Mark resolved';
204
+$txt['shd_ticket_unresolved'] = 'Mark unresolved';
205
+$txt['shd_ticket_assign'] = 'Assign';
206
+$txt['shd_ticket_assign_self'] = 'Assign to me';
207
+$txt['shd_ticket_reassign'] = 'Re-Assign';
208
+$txt['shd_ticket_unassign'] = 'Un-Assign';
209
+$txt['shd_ticket_delete'] = 'Delete';
210
+$txt['shd_delete_confirm'] = 'Are you sure you want to delete this ticket? If deleted, this ticket will be moved to recycling bin.';
211
+$txt['shd_delete_reply_confirm'] = 'Are you sure you want to delete this reply? If deleted, this reply will be moved to the recycling bin.';
212
+$txt['shd_ticket_restore'] = 'Restore';
213
+$txt['shd_delete_permanently'] = 'Delete permanently';
214
+$txt['shd_delete_permanently_confirm'] = 'Are you sure you want to permanently delete this ticket? This CAN NOT be undone!';
215
+$txt['shd_ticket_move_to_topic'] = 'Move to topic';
216
+$txt['shd_actions'] = 'Actions';
217
+$txt['shd_back_to_ticket'] = 'Return to this ticket after posting';
218
+$txt['shd_disable_smileys_post'] = 'Turn off smileys in this post';
219
+
220
+$txt['shd_ticket_assign_ticket'] = 'Assign Ticket';
221
+$txt['shd_ticket_assign_to'] = 'Assign ticket to';
222
+
223
+// Ticket to topic and back
224
+$txt['shd_move_ticket_to_topic'] = 'Move ticket to topic';
225
+$txt['shd_move_ticket'] = 'Move ticket';
226
+$txt['shd_ticket_board'] = 'Board';
227
+$txt['shd_move_send_pm'] = 'Send a PM to the ticket owner';
228
+$txt['shd_move_why'] = 'Please enter a brief description as to
229
+why this ticket is being moved to a forum topic. ';
230
+$txt['shd_ticket_moved_subject'] = 'Your ticket has been moved.';
231
+$txt['shd_move_default'] = 'Hello {user},
232
+
233
+Your ticket, {subject}, has been moved from the helpdesk to a topic in the forum.
234
+You can find your ticket in the board {board} or via this link:
235
+
236
+{link}
237
+
238
+Thanks';
239
+
240
+$txt['shd_move_topic_to_ticket'] = 'Move topic to helpdesk';
241
+$txt['shd_move_topic'] = 'Move topic';
242
+$txt['shd_move_send_pm_topic'] = 'Send a PM to the topic starter';
243
+$txt['shd_move_why_topic'] = 'Please enter a brief description as to
244
+why this topic is being moved to the helpdesk. ';
245
+$txt['shd_ticket_moved_subject_topic'] = 'Your topic has been moved.';
246
+$txt['shd_move_default_topic'] = 'Hello {user},
247
+
248
+Your topic, {subject}, has been moved from the forum to the Helpdesk section.
249
+You can find your topic via this link:
250
+
251
+{link}
252
+
253
+Thanks';
254
+
255
+$txt['shd_ticket_move_deleted'] = 'This ticket has replies that are currently in the recycle bin. What do you wish to do?';
256
+$txt['shd_ticket_move_deleted_abort'] = 'Abort, take me to the recycle bin';
257
+$txt['shd_ticket_move_deleted_delete'] = 'Continue, abandon the deleted replies (do not move them into the new topic)';
258
+$txt['shd_ticket_move_deleted_undelete'] = 'Continue, undelete the replies (move them into the new topic)';
259
+
260
+// Recycling
261
+$txt['shd_recycle_bin'] = 'Recycle Bin';
262
+$txt['shd_recycle_greeting'] = 'This is the recycling bin. All deleted tickets go here, but staff members with special permissions can remove tickets permanently from here.';
263
+
264
+// Posting
265
+$txt['shd_create_ticket'] = 'Create ticket';
266
+$txt['shd_edit_ticket'] = 'Edit ticket';
267
+$txt['shd_edit_ticket_linktree'] = 'Edit ticket (%s)';
268
+$txt['shd_ticket_subject'] = 'Ticket subject';
269
+$txt['shd_ticket_post_error'] = 'The following issue, or issues, occurred while trying to post this ticket';
270
+$txt['shd_reply_ticket'] = 'Reply to ticket';
271
+$txt['shd_reply_ticket_linktree'] = 'Reply to ticket (%s)';
272
+$txt['shd_edit_reply_linktree'] = 'Edit reply (%s)';
273
+$txt['shd_previewing_ticket'] = 'Previewing ticket';
274
+$txt['shd_previewing_reply'] = 'Previewing reply to';
275
+
276
+// Profile / trackip
277
+$txt['shd_replies_from_ip'] = 'Helpdesk replies posted from IP (range)';
278
+$txt['shd_no_replies_from_ip'] = 'No helpdesk replies from the specified IP (range) found';
279
+$txt['shd_replies_from_ip_desc'] = 'Below is a list of all messages posted to the helpdesk from this IP (range).';
280
+$txt['shd_is_ticket_opener'] = ' (ticket starter)';
281
+
282
+?>
0 283
\ No newline at end of file
... ...
@@ -0,0 +1,198 @@
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: 1.0 Felidae                             #
17
+# File Info: SimpleDesk-Admin.english.php / 1.0 Felidae       #
18
+###############################################################
19
+// Version: 1.0 Felidae; SimpleDesk administration options
20
+
21
+// Important! Before editing these language files please read the text at the top of index.english.php.
22
+
23
+/**
24
+ *	This file contains all of the language strings used in SimpleDesk's administration panel which is loaded throughout the SMF admin area.
25
+ *
26
+ *	@package language
27
+ *	@todo Document the text groups in this file.
28
+ *	@since 1.0
29
+ */
30
+
31
+//! @name The 'Core Features' page item
32
+//@{
33
+$txt['core_settings_item_shd'] = 'Helpdesk';
34
+$txt['core_settings_item_shd_desc'] = 'The helpdesk allows you to expand your forum into the service industry by providing a dedicated user-staff helpdesk area.';
35
+//@}
36
+
37
+//! @name Items for general SMF/ACP integration
38
+//@{
39
+$txt['errortype_simpledesk'] = 'SimpleDesk';
40
+//@}
41
+
42
+//! @name Items for the administration menu structure
43
+//@{
44
+// Admin menu items
45
+$txt['shd_admin_info'] = 'Information';
46
+$txt['shd_admin_options'] = 'Options';
47
+$txt['shd_admin_standalone_options'] = 'Standalone Mode';
48
+$txt['shd_admin_actionlog'] = 'Action Log';
49
+$txt['shd_admin_support'] = 'Support';
50
+$txt['shd_admin_helpdesklog'] = 'Helpdesk Log';
51
+
52
+$txt['shd_admin_options_display'] = 'Display Options';
53
+$txt['shd_admin_options_posting'] = 'Posting Options';
54
+$txt['shd_admin_options_admin'] = 'Administrative Options';
55
+$txt['shd_admin_options_standalone'] = 'Standalone Options';
56
+//@}
57
+
58
+//! @name Descriptions for the page items.
59
+//@{
60
+$txt['shd_admin_info_desc'] = 'This is the information center for the helpdesk, powered by SimpleDesk. Here you can get the latest news as well as version-specific support.';
61
+$txt['shd_admin_options_desc'] = 'This is the general configuration area for the helpdesk, where some basic options can be configured.';
62
+$txt['shd_admin_options_display_desc'] = 'In this area you can change some settings that will edit the display of your helpdesk.';
63
+$txt['shd_admin_options_posting_desc'] = 'Here you can edit posting settings, such as BBC, smileys, and attachments.';
64
+$txt['shd_admin_options_admin_desc'] = 'Here you can set some general administrative options for the helpdesk.';
65
+$txt['shd_admin_options_standalone_desc'] = 'This area manages the standalone mode for the helpdesk, that effectively disables the forum part of an SMF installation.';
66
+$txt['shd_admin_actionlog_desc'] = 'This is a list of all actions, such as resolved tickets, edited tickets and more, carried out in the helpdesk.';
67
+$txt['shd_admin_support_desc'] = 'This area will help you get through to SimpleDesk.net quickly and effectively - the post will include some information helpful for our Support team, about your installation (like SMF version and SimpleDesk version).';
68
+$txt['shd_admin_help'] = 'This is the administration panel for the helpdesk. Here you can manage settings, get news and updates on this modification, and view helpdesk logs.';
69
+//@}
70
+
71
+//! @name SimpleDesk info center
72
+//@{
73
+$txt['shd_live_from'] = 'Live from SimpleDesk.net';
74
+$txt['shd_no_connect'] = 'Could not retrieve news file from simpledesk.net';
75
+$txt['shd_current_version'] = 'Current Version';
76
+$txt['shd_your_version'] = 'Your Version';
77
+$txt['shd_mod_information'] = 'Mod Information';
78
+$txt['shd_admin_readmore'] = 'Read more';
79
+$txt['shd_admin_help_live'] = 'This box displays the latest news and updates from www.simpledesk.net. Keep your eyes open for new releases and bug fixes. If a new version of this modification is released, you will also see a notification at the top of the helpdesk administration page.';
80
+$txt['shd_admin_help_modification'] = 'This box contains various information about your installation of SimpleDesk.';
81
+$txt['shd_admin_help_credits'] = 'This box lists all of the people that made SimpleDesk possible, from the developers of the actual code, to the support team and the beta testers.';
82
+$txt['shd_admin_help_update'] = 'If you can see this box, you are most likely using an outdated version of SimpleDesk. Follow the guidelines in the notification in order to upgrade to the new release.';
83
+$txt['shd_ticket_information'] = 'Ticket information';
84
+$txt['shd_total_tickets'] = 'Total number of tickets';
85
+$txt['shd_open_tickets'] = 'Open tickets';
86
+$txt['shd_closed_tickets'] = 'Closed tickets';
87
+$txt['shd_recycled_tickets'] = 'Recycled tickets';
88
+//@}
89
+
90
+//! @name Translatable strings for the credits
91
+//@{
92
+$txt['shd_credits'] = 'SimpleDesk Credits';
93
+$txt['shd_credits_and'] = 'and';
94
+$txt['shd_credits_pretext'] = 'These are the persons that made SimpleDesk possible. Thank you!';
95
+$txt['shd_credits_devs'] = 'Developers';
96
+$txt['shd_credits_devs_desc'] = 'The developers of the actual SimpleDesk code.';
97
+$txt['shd_credits_projectsupport'] = 'Project Support';
98
+$txt['shd_credits_projectsupport_desc'] = 'Those managing and supporting the project in different ways.';
99
+$txt['shd_credits_marketing'] = 'Marketing';
100
+$txt['shd_credits_marketing_desc'] = 'Those spreading the word of SimpleDesk.';
101
+$txt['shd_credits_globalizer'] = 'Globalization';
102
+$txt['shd_credits_globalizer_desc'] = 'The people who makes SimpleDesk spread across the world.';
103
+$txt['shd_credits_support'] = 'Support';
104
+$txt['shd_credits_support_desc'] = 'The people providing all the helpless souls with the support they require.';
105
+$txt['shd_credits_qualityassurance'] = 'Quality Assurance';
106
+$txt['shd_credits_qualityassurance_desc'] = 'The leaders of the beta testing team.';
107
+$txt['shd_credits_beta'] = 'Beta Testers';
108
+$txt['shd_credits_beta_desc'] = 'These persons make sure SimpleDesk lives up to the expectations.';
109
+$txt['shd_credits_alltherest'] = 'Anyone else we might\'ve missed...';
110
+$txt['shd_credits_ledicons'] = 'Creator of the icons used by SimpleDesk';
111
+$txt['shd_credits_user'] = '<strong>YOU</strong>, the proud users of SimpleDesk. Thank you for choosing our software!';
112
+//@}
113
+
114
+//! @name Configuration items on the Display Options page
115
+//@{
116
+$txt['shd_staff_badge'] = 'What style of badges to use in ticket view?';
117
+$txt['shd_staff_badge_note'] = 'When looking at different replies, it may be helpful to display badges if you have different teams who may respond in the helpdesk. It may also be useful to display members\' own badges, or not; this option lets you select.';
118
+$txt['shd_staff_badge_nobadge'] = 'Display no badge, just a small icon for staff';
119
+$txt['shd_staff_badge_staffbadge'] = 'Display badges only of staff members';
120
+$txt['shd_staff_badge_userbadge'] = 'Display badges only of non-staff/regular users';
121
+$txt['shd_staff_badge_bothbadge'] = 'Display badges of both users and staff';
122
+$txt['shd_display_avatar'] = 'Display avatars in replies to a ticket?';
123
+$txt['shd_ticketnav_style'] = 'What type of navigation to use in the ticket view?';
124
+$txt['shd_ticketnav_style_note'] = 'When looking at tickets, there may be a number of options available to users, including edit, close, and delete. This option specifies the different ways this can look.';
125
+$txt['shd_ticketnav_style_sd'] = 'SimpleDesk style (icon with small text note)';
126
+$txt['shd_ticketnav_style_sdcompact'] = 'SimpleDesk style (icon only)';
127
+$txt['shd_ticketnav_style_smf'] = 'SMF style (text buttons, above the ticket)';
128
+//@}
129
+
130
+//! @name Configuration items on the Posting Options page
131
+//@{
132
+$txt['shd_allow_ticket_bbc'] = 'Allow tickets and replies to use bbcode';
133
+$txt['shd_allow_ticket_smileys'] = 'Allow tickets and replies to use smileys';
134
+$txt['shd_attachments_mode'] = 'How should attachments to tickets be treated?';
135
+$txt['shd_attachments_mode_ticket'] = 'As attached to the ticket';
136
+$txt['shd_attachments_mode_reply'] = 'As attached to individual replies';
137
+$txt['shd_attachments_mode_note'] = 'If using "to ticket" mode, there is no limit on the number of attachments, while if using "to replies", the helpdesk will use the same settings as regular attachments, by default 4 to a post only. Both modes check the size per attachment and that it will not fill up your attachments folder based on the settings in your attachments panel.';
138
+$txt['shd_bbc'] = 'Enabled BBC tags in the helpdesk';
139
+$txt['shd_bbc_desc'] = '<strong>If BBC is enabled above.</strong> What tags should be enabled for use in the helpdesk?';
140
+//@}
141
+
142
+//! @name Configuration items on the Admin Options page
143
+//@{
144
+$txt['shd_disable_action_log'] = 'Disable logging of helpdesk actions?';
145
+$txt['shd_staff_ticket_self'] = 'For tickets opened by staff, should it be possible to assign them the ticket?';
146
+$txt['shd_admins_not_assignable'] = 'Should admins be excluded from having tickets assigned to them?';
147
+$txt['shd_privacy_display'] = 'What method to use for displaying ticket privacy?';
148
+$txt['shd_privacy_display_smart'] = 'Display a ticket\'s privacy setting when appropriate';
149
+$txt['shd_privacy_display_always'] = 'Always display the ticket\'s privacy setting';
150
+$txt['shd_privacy_display_note'] = 'Normally tickets are limited to user seeing their own and staff seeing all users. There are times you might want staff to be able to create tickets only for senior staff to see - this is a "private" ticket. Since "non-private" might be confusing for regular users, this option allows you to hide the display of "non private" or "private" to only when it is appropriate on a ticket.';
151
+//@}
152
+
153
+//! @name Configuration items on the Standalone Options page
154
+//@{
155
+$txt['shd_helpdesk_only'] = 'Enable helpdesk only mode';
156
+$txt['shd_helpdesk_only_note'] = 'This will disable access to topics and boards, as well as optionally the features below. Note that none of the data is lost, merely rendered inactive. The following options ONLY apply when this mode is active (when the forum is basically disabled outside the helpdesk)';
157
+$txt['shd_disable_pm'] = 'Disable private messages entirely';
158
+$txt['shd_disable_mlist'] = 'Disable the memberlist entirely';
159
+//@}
160
+
161
+//! @name General language strings for the action log (entries are contained in SimpleDesk-LogAction.english.php)
162
+//@{
163
+$txt['shd_delete_item'] = 'Delete this log item';
164
+$txt['shd_admin_actionlog_title'] = 'Helpdesk action log';
165
+$txt['shd_admin_actionlog_action'] = 'Action';
166
+$txt['shd_admin_actionlog_date'] = 'Date';
167
+$txt['shd_admin_actionlog_member'] = 'Member';
168
+$txt['shd_admin_actionlog_position'] = 'Position';
169
+$txt['shd_admin_actionlog_ip'] = 'IP';
170
+$txt['shd_admin_actionlog_none'] = 'No entries were found.';
171
+$txt['shd_admin_actionlog_unknown'] = 'Unknown';
172
+$txt['shd_admin_actionlog_removeall'] = 'Empty out the entire log';
173
+$txt['shd_admin_actionlog_removeall_confirm'] = 'This will permanently delete all entries in the action log older than %s hours. Are you sure?';
174
+//@}
175
+
176
+//! @name Strings for the post-to-SimpleDesk.net support page
177
+//@{
178
+$txt['shd_admin_support_form_title'] = 'Support form';
179
+$txt['shd_admin_support_what_is_this'] = 'What is this?';
180
+$txt['shd_admin_support_explanation'] = 'This simple form will allow you to send a support request directly to the SimpleDesk website so that the support team there can help you solve any issue you run in to.<br /><br />Please note that you will need an account on our website in order to post as well as replying to your topic in the future. This form will simply speed up the posting process.';
181
+$txt['shd_admin_support_send'] = 'Send support request';
182
+//@}
183
+
184
+//! @name The browse-attachments integration strings
185
+//@{
186
+$txt['attachment_manager_shd_attach'] = 'Helpdesk attachments';
187
+$txt['attachment_manager_shd_thumb'] = 'Helpdesk thumbnails';
188
+$txt['attachment_manager_shd_attach_no_entries'] = 'There are currently no helpdesk attachments.';
189
+$txt['attachment_manager_shd_thumb_no_entries'] = 'There are currently no helpdesk thumbnails.';
190
+//@}
191
+
192
+/**
193
+ *	@ignore
194
+ *	Warning: He may bite.
195
+*/
196
+$txt['shd_fluffy'] = 'Guardian of the <span %s>cookies</span>';
197
+
198
+?>
0 199
\ No newline at end of file
... ...
@@ -0,0 +1,78 @@
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: 1.0 Felidae                             #
17
+# File Info: SimpleDesk-LogAction.english.php / 1.0 Felidae   #
18
+###############################################################
19
+// Version: 1.0 Felidae; SimpleDesk action log
20
+
21
+// Important! Before editing these language files please read the text at the top of index.english.php.
22
+
23
+/**
24
+ *	This file contains all of the base language strings used by the helpdesk action log.
25
+ *	Unlike other language files, many of the strings here are parameterised, enabling them to be extended in the future.
26
+ *	@see shd_log_action()
27
+ *
28
+ *	@package language
29
+ *	@todo Document the text groups in this file.
30
+ *	@since 1.0
31
+ */
32
+
33
+//! @name General strings
34
+//@{
35
+$txt['shd_action_log_disabled'] = '<strong>Note:</strong> Logging of actions is currently <strong>disabled</strong>, so no new log entries will be added.';
36
+//@}
37
+
38
+//! @name Ticket resolution
39
+//@{
40
+$txt['shd_log_resolve'] = '<a href="{scripturl}?action=helpdesk;sa=ticket;ticket={ticket}">{subject}</a> marked as <strong>resolved</strong>.';
41
+$txt['shd_log_unresolve'] = '<a href="{scripturl}?action=helpdesk;sa=ticket;ticket={ticket}">{subject}</a> marked as <strong>not yet resolved</strong>.';
42
+//@}
43
+
44
+//! @name Ticket assignation
45
+//@{
46
+$txt['shd_log_assign'] = 'Assigned <a href="{scripturl}?action=helpdesk;sa=ticket;ticket={ticket}">{subject}</a> to {profile_link}';
47
+$txt['shd_log_unassign'] = 'Assigned <a href="{scripturl}?action=helpdesk;sa=ticket;ticket={ticket}">{subject}</a> to no-one';
48
+//@}
49
+
50
+//! @name Ticket privacy
51
+//@{
52
+$txt['shd_log_markprivate'] = '<a href="{scripturl}?action=helpdesk;sa=ticket;ticket={ticket}">{subject}</a> marked as <strong>private</strong>.';
53
+$txt['shd_log_marknotprivate'] = '<a href="{scripturl}?action=helpdesk;sa=ticket;ticket={ticket}">{subject}</a> marked as <strong>not private</strong>.';
54
+//@}
55
+
56
+//! @name Ticket urgency
57
+//@{
58
+$txt['shd_log_urgency_increase'] = '<a href="{scripturl}?action=helpdesk;sa=ticket;ticket={ticket}">{subject}</a> increased to <strong>{urgency}</strong>.';
59
+$txt['shd_log_urgency_decrease'] = '<a href="{scripturl}?action=helpdesk;sa=ticket;ticket={ticket}">{subject}</a> decreased to <strong>{urgency}</strong>.';
60
+//@}
61
+
62
+//! @name Ticket/topic, topic/ticket moves
63
+//@{
64
+$txt['shd_log_tickettotopic'] = 'Moved <a href="{scripturl}?topic={ticket}.0">{subject}</a> to <strong><a href="{scripturl}?board={board_id}.0">{board_name}</a></strong> in the forum';
65
+$txt['shd_log_topictoticket'] = 'Moved the topic <strong><a href="{scripturl}?action=helpdesk;sa=ticket;ticket={ticket}">{subject}</a></strong> from the forum to the helpdesk.';
66
+//@}
67
+
68
+//! @name Ticket deletion, restoration, permadeletion
69
+//@{
70
+$txt['shd_log_delete'] = 'Deleted <a href="{scripturl}?action=helpdesk;sa=ticket;ticket={ticket}.0">{subject}</a> to recycle bin.';
71
+$txt['shd_log_restore'] = 'Restored <a href="{scripturl}?action=helpdesk;sa=ticket;ticket={ticket}.0">{subject}</a> from recycle bin.';
72
+$txt['shd_log_permadelete'] = '<strong>Permanently</strong> deleted "{subject}" (ticket {ticket}).';
73
+$txt['shd_log_delete_reply'] = 'Deleted reply in <a href="{scripturl}?action=helpdesk;sa=ticket;ticket={ticket}.msg{msg}#msg{msg};recycle">{subject}</a> to recycle bin.';
74
+$txt['shd_log_restore_reply'] = 'Restored reply in <a href="{scripturl}?action=helpdesk;sa=ticket;ticket={ticket}.msg{msg}#msg{msg}">{subject}</a> from recycle bin.';
75
+$txt['shd_log_permadelete_reply'] = '<strong>Permanently</strong> deleted a reply from <a href="{scripturl}?action=helpdesk;sa=ticket;ticket={ticket}">{subject}</a>.';
76
+//@}
77
+
78
+?>
0 79
\ No newline at end of file
... ...
@@ -0,0 +1,253 @@
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: 1.0 Felidae                             #
17
+# File Info: SimpleDesk-Permissions.english.php / 1.0 Felidae #
18
+###############################################################
19
+// Version: 1.0 Felidae; SimpleDesk permissions
20
+
21
+// Important! Before editing these language files please read the text at the top of index.english.php.
22
+
23
+/**
24
+ *	This file contains all of the language strings (both main description and help text) for all permissions that SimpleDesk adds to the
25
+ *	permissions pages. Note that both classic and simple view are accounted for, explaining the large number of strings per permission.
26
+ *
27
+ *	@package language
28
+ *	@todo Document the text groups in this file.
29
+ *	@since 1.0
30
+ */
31
+
32
+//! @name Permission groups
33
+//@{
34
+$txt['permissiongroup_helpdesk'] = 'Helpdesk permissions';
35
+$txt['permissiongroup_simple_use_helpdesk'] = 'Permissions to use the helpdesk system';
36
+
37
+$txt['permissiongroup_shd_staff'] = 'Helpdesk staff permissions';
38
+$txt['permissiongroup_simple_shd_staff'] = 'Permissions for staff users of the helpdesk';
39
+
40
+$txt['permissiongroup_shd_admin'] = 'Helpdesk administration';
41
+$txt['permissiongroup_simple_shd_admin'] = 'Permissions for administrating the helpdesk';
42
+//@}
43
+
44
+//! @name General permissions
45
+//@{
46
+$txt['permissionname_access_helpdesk'] = 'Access the helpdesk';
47
+$txt['permissionhelp_access_helpdesk'] = 'This permission allows users to even see the helpdesk, and is required to do anything within the helpdesk; is available in the event of banning someone from the helpdesk.';
48
+$txt['permissionname_admin_helpdesk'] = 'Administrate the helpdesk (allows reconfiguration)';
49
+$txt['permissionhelp_admin_helpdesk'] = 'This permission controls whether administrative functions of the helpdesk can be passed on to users who are not administrators of the forum; typically senior associates/helpdesk agents would have this.';
50
+$txt['permissionname_shd_staff'] = 'Treat this user group as helpdesk staff';
51
+$txt['permissionhelp_shd_staff'] = 'Users with this permission will be treated as staff. Amongst other things, this grants the ability to reply to any ticket that is not owned by the starter.';
52
+//@}
53
+
54
+//! @name Ticket visibility: general
55
+//@{
56
+$txt['permissionname_shd_view_ticket'] = 'View helpdesk tickets';
57
+$txt['permissionhelp_shd_view_ticket'] = 'This permission controls whether users can see tickets at all, and more importantly whether they have access to the standard user view or the staff level view; users with the ability to view any are generally considered to be staff.';
58
+$txt['permissionname_shd_view_ticket_own'] = 'Own tickets';
59
+$txt['permissionname_shd_view_ticket_any'] = 'Any tickets';
60
+$txt['permissionname_simple_shd_view_ticket_own'] = 'Allow users to view their own public tickets';
61
+$txt['permissionhelp_simple_shd_view_ticket_own'] = 'This permission allows regular users to see their own tickets, but only those not marked private.';
62
+$txt['permissionname_simple_shd_view_ticket_any'] = 'Allow users to view all public helpdesk tickets';
63
+$txt['permissionhelp_simple_shd_view_ticket_any'] = 'This permission allows users to see any normal ticket on the helpdesk.';
64
+//@}
65
+
66
+//! @name Ticket visibility: privacy
67
+//@{
68
+$txt['permissionname_shd_view_ticket_private'] = 'View private helpdesk tickets';
69
+$txt['permissionhelp_shd_view_ticket_private'] = 'This permission controls whether tickets that are marked private can be seen by users, for example, you could use this permission for staff members who want to post a ticket that concerns them, without it being visible to all staff - only senior staff.';
70
+$txt['permissionname_shd_view_ticket_private_own'] = 'Own private tickets';
71
+$txt['permissionname_shd_view_ticket_private_any'] = 'Any users\' private tickets';
72
+$txt['permissionname_simple_shd_view_ticket_private_own'] = 'Allow users to view their own private tickets';
73
+$txt['permissionhelp_simple_shd_view_ticket_private_own'] = 'This permission allows users to see their own private tickets.';
74
+$txt['permissionname_simple_shd_view_ticket_private_any'] = 'Allow users to view all private tickets';
75
+$txt['permissionhelp_simple_shd_view_ticket_private_any'] = 'This permission allows users to see all of the private tickets, in addition to any other permissions they have. Recommended for senior staff.';
76
+//@}
77
+
78
+//! @name Ticket modifications: privacy
79
+//@{
80
+$txt['permissionname_shd_alter_privacy'] = 'Allow users to alter the privacy on a ticket';
81
+$txt['permissionhelp_shd_alter_privacy'] = 'This permission allows a user to change the privacy on a ticket, from public to private and back. Actual ticket editing permission is not required to use this.';
82
+$txt['permissionname_shd_alter_privacy_own'] = 'Own tickets';
83
+$txt['permissionname_shd_alter_privacy_any'] = 'Any tickets';
84
+$txt['permissionname_simple_shd_alter_privacy_own'] = 'Allow users to mark their own tickets as private/public';
85
+$txt['permissionhelp_simple_shd_alter_privacy_own'] = 'This allows users to flag their own tickets as private or public, in line with the ability to view private tickets system. (This does not require the ability to edit ticket details.)';
86
+$txt['permissionname_simple_shd_alter_privacy_any'] = 'Allow users to mark their any tickets as private/public';
87
+$txt['permissionhelp_simple_shd_alter_privacy_any'] = 'This allows users to flag any users\' tickets as private or public, in line with the ability to view private tickets system. (This does not require the ability to edit ticket details.)';
88
+//@}
89
+
90
+//! @name Ticket modifications: urgency
91
+//@{
92
+$txt['permissionname_shd_alter_urgency'] = 'Allow users to alter the urgency of tickets';
93
+$txt['permissionhelp_shd_alter_urgency'] = 'Tickets are available in a range of "urgency" levels: Low, Medium, High, Very High, Severe, Critical. This permission allows users to raise and lower urgency up to the level of High (and lower from Very High back to High only)';
94
+$txt['permissionname_shd_alter_urgency_own'] = 'Own tickets';
95
+$txt['permissionname_shd_alter_urgency_any'] = 'Any tickets';
96
+$txt['permissionname_simple_shd_alter_urgency_own'] = 'Allow users to raise or lower the urgency of their own tickets';
97
+$txt['permissionhelp_simple_shd_alter_urgency_own'] = 'Tickets are available in a range of "urgency" levels: Low, Medium, High, Very High, Severe, Critical. This permission allows users to raise and lower urgency of their own tickets up to the level of High (and lower from Very High back to High only)';
98
+$txt['permissionname_simple_shd_alter_urgency_any'] = 'Allow users to raise or lower the urgency of any tickets';
99
+$txt['permissionhelp_simple_shd_alter_urgency_any'] = 'Tickets are available in a range of "urgency" levels: Low, Medium, High, Very High, Severe, Critical. This permission allows users to raise and lower urgency of any tickets in the helpdesk up to the level of High (and lower from Very High back to High only)';
100
+$txt['permissionname_shd_alter_urgency_higher'] = 'Allow user to set urgency "Very High" or above';
101
+$txt['permissionhelp_shd_alter_urgency_higher'] = 'This permission extends the above permission, and allows users to be able to select the highest levels of urgency for a ticket. Generally this would be assigned by staff only.';
102
+$txt['permissionname_shd_alter_urgency_higher_own'] = 'Own tickets';
103
+$txt['permissionname_shd_alter_urgency_higher_any'] = 'Any tickets';
104
+$txt['permissionname_simple_shd_alter_urgency_higher_own'] = 'Allow users to set the urgency of their own tickets to "Very High" or above';
105
+$txt['permissionhelp_simple_shd_alter_urgency_higher_any'] = 'Urgency of tickets is split into two tiers, Low/Medium/High, and Very High/Severe/Critical. This permission allows users to flag their own tickets higher than High.';
106
+$txt['permissionname_simple_shd_alter_urgency_higher_any'] = 'Allow users to set the urgency of any tickets to "Very High" or above';
107
+$txt['permissionhelp_simple_shd_alter_urgency_higher_any'] = 'Urgency of tickets is split into two tiers, Low/Medium/High, and Very High/Severe/Critical. This permission allows users to flag any tickets more urgent than High urgency.';
108
+//@}
109
+
110
+//! @name Ticket modification: assignment
111
+//@{
112
+$txt['permissionname_shd_assign_ticket'] = 'Allow users to assign a ticket';
113
+$txt['permissionhelp_shd_assign_ticket'] = 'This permission allows users to assign tickets to staff to be dealt with; normally would be given to staff to assign tickets to themselves, or to managers to assign to other users';
114
+$txt['permissionname_shd_assign_ticket_own'] = 'To themselves';
115
+$txt['permissionname_shd_assign_ticket_any'] = 'Any staff member';
116
+$txt['permissionname_simple_shd_assign_ticket_own'] = 'Allow users to assign tickets to themselves';
117
+$txt['permissionhelp_simple_shd_assign_ticket_own'] = 'This permission allows a user to assign a ticket to themselves for resolution. Would be useful for lower-level staff.';
118
+$txt['permissionname_simple_shd_assign_ticket_any'] = 'Allow users to assign tickets to any staff memmber';
119
+$txt['permissionhelp_simple_shd_assign_ticket_any'] = 'This permission allows a user to assign a ticket to any staff member, including themselves. Certainly higher level staff would normally have access, but for smaller numbers of staff, all may be granted permission to use it.';
120
+//@}
121
+
122
+//! @name Ticket modification: resolution
123
+//@{
124
+$txt['permissionname_shd_resolve_ticket'] = 'Allow users to mark a ticket as resolved';
125
+$txt['permissionhelp_shd_resolve_ticket'] = 'This permission allows users to mark tickets resolved, and mark completed tickets as unresolved if there is further business to attend to.';
126
+$txt['permissionname_shd_resolve_ticket_own'] = 'Own tickets';
127
+$txt['permissionname_shd_resolve_ticket_any'] = 'Any tickets';
128
+$txt['permissionname_simple_shd_resolve_ticket_own'] = 'Allow users to mark their own tickets as resolved';
129
+$txt['permissionhelp_simple_shd_resolve_ticket_own'] = 'This permission allows a user to mark their own tickets as resolved, and additionally to mark any completed tickets as unresolved if further things need to be attended to.';
130
+$txt['permissionname_simple_shd_resolve_ticket_any'] = 'Allow users to mark any ticket as resolved';
131
+$txt['permissionhelp_simple_shd_resolve_ticket_any'] = 'This permission allows a user to mark any ticket as resolved, and additionally reopen any already-resolved ticket.';
132
+//@}
133
+
134
+//! @name Ticket posting: new ticket
135
+//@{
136
+$txt['permissionname_shd_new_ticket'] = 'Allow users to post a new ticket';
137
+$txt['permissionhelp_shd_new_ticket'] = 'This permission allows users in this membergroup to file new tickets for themselves.';
138
+//@}
139
+
140
+//! @name Ticket posting: replies
141
+//@{
142
+$txt['permissionname_shd_reply_ticket'] = 'Allow users to reply to tickets';
143
+$txt['permissionhelp_shd_reply_ticket'] = 'This permission allows users to reply to tickets, either to provide further details, or to provide an answer.';
144
+$txt['permissionname_shd_reply_ticket_own'] = 'Own tickets';
145
+$txt['permissionname_shd_reply_ticket_any'] = 'Any tickets';
146
+$txt['permissionname_simple_shd_reply_ticket_own'] = 'Allow users to reply to their own tickets';
147
+$txt['permissionhelp_simple_shd_reply_ticket_own'] = 'This permission allows a user to reply to tickets that they have started. You would normally grant this to membergroups who can start tickets, as it allows them to reply to staff comments.';
148
+$txt['permissionname_simple_shd_reply_ticket_any'] = 'Allow users to reply to any ticket';
149
+$txt['permissionhelp_simple_shd_reply_ticket_any'] = 'This permission allows a user to reply to any ticket they have access to (see the permissions about being able to view tickets). This would normally be granted to membergroups that are considered staff.';
150
+//@}
151
+
152
+//! @name Ticket posting: editing a ticket
153
+//@{
154
+$txt['permissionname_shd_edit_ticket'] = 'Allow users to edit the ticket details';
155
+$txt['permissionhelp_shd_edit_ticket'] = 'This permission allows a user to edit the core details of a ticket, such as the ticket\'s name and initial description.';
156
+$txt['permissionname_shd_edit_ticket_own'] = 'Own tickets';
157
+$txt['permissionname_shd_edit_ticket_any'] = 'Any tickets';
158
+$txt['permissionname_simple_shd_edit_ticket_own'] = 'Allow users to edit their own tickets\' details';
159
+$txt['permissionhelp_simple_shd_edit_ticket_own'] = 'This permission allows a user to edit the basic ticket details of their own tickets, the title and body contents.';
160
+$txt['permissionname_simple_shd_edit_ticket_any'] = 'Allow users to edit any ticket\'s details';
161
+$txt['permissionhelp_simple_shd_edit_ticket_any'] = 'This permission allows a user to edit the basic ticket details (title and body contents) of any ticket.';
162
+//@}
163
+
164
+//! @name Ticket posting: editing replies
165
+//@{
166
+$txt['permissionname_shd_edit_reply'] = 'Allow users to edit replies in tickets';
167
+$txt['permissionhelp_shd_edit_reply'] = 'This permission allows a user to edit replies in tickets, not the main ticket itself.';
168
+$txt['permissionname_shd_edit_reply_own'] = 'Own replies';
169
+$txt['permissionname_shd_edit_reply_any'] = 'Any replies';
170
+$txt['permissionname_simple_shd_edit_reply_own'] = 'Allow users to edit their own replies to tickets';
171
+$txt['permissionhelp_simple_shd_edit_reply_own'] = 'This permission allows users to update the contents of replies they make to tickets.';
172
+$txt['permissionname_simple_shd_edit_reply_any'] = 'Allow users to edit any tickets and posts';
173
+$txt['permissionhelp_simple_shd_edit_reply_any'] = 'This permission allows users to update the contents of any replies to any tickets they have access to.';
174
+//@}
175
+
176
+//! @name Ticket deletion: tickets
177
+//@{
178
+$txt['permissionname_shd_delete_ticket'] = 'Allow users to delete a ticket';
179
+$txt['permissionhelp_shd_delete_ticket'] = 'This permission allows a user to delete a ticket.';
180
+$txt['permissionname_shd_delete_ticket_own'] = 'Own tickets';
181
+$txt['permissionname_shd_delete_ticket_any'] = 'Any tickets';
182
+$txt['permissionname_simple_shd_delete_ticket_own'] = 'Allow users to delete their own tickets';
183
+$txt['permissionhelp_simple_shd_delete_ticket_own'] = 'This permission allows a user to delete their own tickets. Note that this is not a permanent deletion - all "deleted" tickets are moved to a recycle bin.';
184
+$txt['permissionname_simple_shd_delete_ticket_any'] = 'Allow users to delete any tickets';
185
+$txt['permissionhelp_simple_shd_delete_ticket_any'] = 'This permission allows a user to delete their own tickets. Note that this is not a permanent deletion - all "deleted" tickets are moved to a recycle bin.';
186
+//@}
187
+
188
+//! @name Ticket deletion: replies
189
+//@{
190
+$txt['permissionname_shd_delete_reply'] = 'Allow users to delete replies in tickets';
191
+$txt['permissionhelp_shd_delete_reply'] = 'This permission allows a user to delete replies in tickets, not the main ticket itself.';
192
+$txt['permissionname_shd_delete_reply_own'] = 'Own replies';
193
+$txt['permissionname_shd_delete_reply_any'] = 'Any replies';
194
+$txt['permissionname_simple_shd_delete_reply_own'] = 'Allow users to delete their own replies to tickets';
195
+$txt['permissionhelp_simple_shd_delete_reply_own'] = 'This permission allows users to remove any replies they have made to a ticket. Note that this is not a permanent deletion - all "deleted" tickets are moved to a recycle bin.';
196
+$txt['permissionname_simple_shd_delete_reply_any'] = 'Allow users to delete any replies to tickets';
197
+$txt['permissionhelp_simple_shd_delete_reply_any'] = 'This permission allows users to the replies of any user to any ticket they can access. Note that this is not a permanent deletion - all "deleted" tickets are moved to a recycle bin.';
198
+//@}
199
+
200
+//! @name Ticket deletion: permadelete
201
+//@{
202
+$txt['permissionname_shd_delete_recycling'] = 'Delete tickets/posts from recycling';
203
+$txt['permissionhelp_shd_delete_recycling'] = 'This permission allows users to remove tickets and posts from the recycling area. Note that once removed here, they are NOT recoverable, as such it is not recommended to grant this permission to all staff.';
204
+//@}
205
+
206
+//! @name Ticket deletion: accessing recycle bin
207
+//@{
208
+$txt['permissionname_shd_access_recyclebin'] = 'Access recycling bin';
209
+$txt['permissionhelp_shd_access_recyclebin'] = 'Users with this permission will be able to access the list of recycled tickets and read the tickets.';
210
+//@}
211
+
212
+//! @name Ticket deletion: restoration of tickets
213
+//@{
214
+$txt['permissionname_shd_restore_ticket'] = 'Allow user to restore tickets from the recycling bin';
215
+$txt['permissionhelp_shd_restore_ticket'] = 'This permission allows a user to restore deleted tickets from the recycling bin.';
216
+$txt['permissionname_shd_restore_ticket_own'] = 'Own tickets';
217
+$txt['permissionname_shd_restore_ticket_any'] = 'Any tickets';
218
+$txt['permissionname_simple_shd_restore_ticket_own'] = 'Allow users to restore their own tickets from recycling';
219
+$txt['permissionhelp_simple_shd_restore_ticket_own'] = 'When tickets are deleted, they are not normally deleted permanently, just saved in a recycling area, from which users can recover them until the administrator or suitably privileged user can delete them (see "permanently delete"). This permission allows users to recover their own tickets out of the recycling area.';
220
+$txt['permissionname_simple_shd_restore_ticket_any'] = 'Allow users to restore any tickets from recycling';
221
+$txt['permissionhelp_simple_shd_restore_ticket_any'] = 'When tickets are deleted, they are not normally deleted permanently, just saved in a recycling area, from which users can recover them until the administrator or suitably privileged user can delete them (see "permanently delete"). This permission allows users to recover any tickets they would have access to out of the recycling area.';
222
+//@}
223
+
224
+//! @name Ticket deletion: restoration of replies
225
+//@{
226
+$txt['permissionname_shd_restore_reply'] = 'Allow user to restore replies in deleted tickets from the recycling bin';
227
+$txt['permissionhelp_shd_restore_reply'] = 'This permission allows a user to restore deleted replies from the recycling bin.';
228
+$txt['permissionname_shd_restore_reply_own'] = 'Own replies';
229
+$txt['permissionname_shd_restore_reply_any'] = 'Any replies';
230
+$txt['permissionname_simple_shd_restore_reply_own'] = 'Allow users to restore their own replies in tickets from recycling';
231
+$txt['permissionhelp_simple_shd_restore_reply_own'] = 'When tickets are deleted, they are not normally deleted permanently, just saved in a recycling area, from which users can recover them until the administrator or suitably privileged user can delete them (see "permanently delete"). This permission allows users to recover replies to tickets - their own replies, note, from tickets out of the recycling area.';
232
+$txt['permissionname_simple_shd_restore_reply_any'] = 'Allow users to restore any replies from tickets in recycling';
233
+$txt['permissionhelp_simple_shd_restore_reply_any'] = 'When tickets are deleted, they are not normally deleted permanently, just saved in a recycling area, from which users can recover them until the administrator or suitably privileged user can delete them (see "permanently delete"). This permission allows users to recover any replies back to regular tickets, from tickets they would have access to out of the recycling area.';
234
+//@}
235
+
236
+//! @name Attachments
237
+//@{
238
+$txt['permissionname_shd_post_attachment'] = 'Allow users to post attachments';
239
+$txt['permissionhelp_shd_post_attachment'] = 'This permission allows a user to post attachments to tickets.';
240
+$txt['permissionname_simple_shd_post_attachment'] = 'Allow users to attach their files to tickets';
241
+$txt['permissionhelp_simple_shd_post_attachment'] = 'This permission allows users the ability to add files to tickets that you have, which might be screenshots of problems, or information that relates to their ticket, or other such information.';
242
+//@}
243
+
244
+//! @name Ticket to topic, topic to ticket movement
245
+//@{
246
+global $modSettings;
247
+$txt['permissionname_shd_ticket_to_topic'] = 'Move tickets to topics';
248
+$txt['permissionhelp_shd_ticket_to_topic'] = 'This permission allows a user to transform tickets into topics in the forum. This should only be granted to staff members.';
249
+$txt['permissionname_shd_topic_to_ticket'] = 'Move topics to tickets';
250
+$txt['permissionhelp_shd_topic_to_ticket'] = 'This permission allows a user to transform topics in the forum into helpdesk tickets. This should only be granted to staff members.';
251
+//@}
252
+
253
+?>
0 254
\ No newline at end of file
... ...
@@ -0,0 +1,82 @@
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: 1.0 Felidae                             #
17
+# File Info: SimpleDesk-Who.english.php / 1.0 Felidae         #
18
+###############################################################
19
+// Version: 1.0 Felidae; SimpleDesk who's online
20
+
21
+// Important! Before editing these language files please read the text at the top of index.english.php.
22
+
23
+/**
24
+ *	This file contains all of the language strings used by Who's Online for actions carried out within the helpdesk.
25
+ *
26
+ *	@package language
27
+ *	@todo Document the text groups in this file.
28
+ *	@since 1.0
29
+ */
30
+
31
+//! @name General strings
32
+//@{
33
+$txt['whoall_helpdesk'] = 'Viewing the helpdesk index.';
34
+$txt['whoall_helpdesk_main'] = 'Viewing the helpdesk index.';
35
+$txt['whoall_helpdesk_ticket'] = 'Viewing a helpdesk ticket.';
36
+//@}
37
+
38
+//! @name Posting/editing
39
+//@{
40
+$txt['whoall_helpdesk_newticket'] = 'Posting a new ticket in the helpdesk.';
41
+$txt['whoall_helpdesk_editticket'] = 'Editing a ticket in the helpdesk.';
42
+$txt['whoall_helpdesk_saveticket'] = 'Posting a ticket in the helpdesk.';
43
+$txt['whoall_helpdesk_reply'] = 'Replying to a helpdesk ticket.';
44
+$txt['whoall_helpdesk_savereply'] = 'Replying to a helpdesk ticket.';
45
+$txt['whoall_helpdesk_editreply'] = 'Editing a reply on a helpdesk ticket.';
46
+$txt['whoall_helpdesk_markunread'] = 'Marking a helpdesk ticket as unread.';
47
+//@}
48
+
49
+//! @name Updating ticket details
50
+//@{
51
+$txt['whoall_helpdesk_assign'] = 'Assigning a ticket to a user in the helpdesk.';
52
+$txt['whoall_helpdesk_assign2'] = 'Assigning a ticket to a user in the helpdesk.';
53
+$txt['whoall_helpdesk_ajax'] = 'Updating a ticket in the helpdesk.';
54
+$txt['whoall_helpdesk_privacychange'] = 'Updating a ticket\'s details in the helpdesk.';
55
+$txt['whoall_helpdesk_urgencychange'] = 'Updating a ticket\'s details in the helpdesk.';
56
+//@}
57
+
58
+//! @name Deletion/recycling, restoration, permadeletion
59
+//@{
60
+$txt['whoall_helpdesk_deletereply'] = 'Deleting a reply to a helpdesk ticket.';
61
+$txt['whoall_helpdesk_deleteticket'] = 'Deleting a ticket in the helpdesk.';
62
+$txt['whoall_helpdesk_restorereply'] = 'Restoring a post from the helpdesk recycle bin.';
63
+$txt['whoall_helpdesk_restoretopic'] = 'Restoring a ticket from the helpdesk recycle bin.';
64
+$txt['whoall_helpdesk_recyclebin'] = 'Viewing the deleted tickets in the helpdesk.';
65
+$txt['whoall_helpdesk_permadelete'] = 'Permanently deleting a ticket from the helpdesk.';
66
+//@}
67
+
68
+//! @name Ticket resolution
69
+//@{
70
+$txt['whoall_helpdesk_resolveticket'] = 'Marking a ticket as resolved, or reopening a resolved, ticket in the helpdesk.';
71
+$txt['whoall_helpdesk_closedtickets'] = 'Viewing the resolved tickets in the helpdesk.';
72
+//@}
73
+
74
+//! @name Topic-to-ticket, ticket-to-topic movement
75
+//@{
76
+$txt['whoall_helpdesk_tickettotopic'] = 'Moving a helpdesk ticket to a forum topic.';
77
+$txt['whoall_helpdesk_tickettotopic2'] = 'Moving a helpdesk ticket to a forum topic.';
78
+$txt['whoall_helpdesk_topictoticket'] = 'Moving a forum topic to a helpdesk ticket.';
79
+$txt['whoall_helpdesk_topictoticket2'] = 'Moving a forum topic to a helpdesk ticket.';
80
+//@}
81
+
82
+?>
0 83
\ No newline at end of file
... ...
@@ -0,0 +1,52 @@
1
+[url=http://www.simpledesk.net/][img]http://www.simpledesk.net/promo/graphics/signatures/signature1.png[/img][/url]
2
+
3
+Thank you for downloading SimpleDesk :)
4
+
5
+[b]License[/b]
6
+
7
+Yes, we know, you don't want the boring formality of a license, but to protect us - and you - we have a license in the code. Clicking the install button after this constitutes acceptance of the following terms.
8
+
9
+[code]Copyright (c) 2010, SimpleDesk Team
10
+All rights reserved.
11
+
12
+Redistribution and use in source and binary forms, with or without
13
+modification, are permitted provided that the following conditions are met:
14
+    * Redistributions of source code must retain the above copyright
15
+      notice, this list of conditions and the following disclaimer.
16
+    * Redistributions in binary form must reproduce the above copyright
17
+      notice, this list of conditions and the following disclaimer in the
18
+      documentation and/or other materials provided with the distribution.
19
+    * Neither the name of SimpleDesk nor the
20
+      names of its contributors may be used to endorse or promote products
21
+      derived from this software without specific prior written permission.
22
+
23
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
24
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26
+DISCLAIMED. IN NO EVENT SHALL SIMPLEDESK TEAM BE LIABLE FOR ANY
27
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.[/code]
33
+
34
+Loosely translated (and this bit isn't the legal wording)
35
+* You can pass it on to others but you have to leave the copyright in the files, including derived/modified versions
36
+
37
+* You can't use the name of SimpleDesk to promote something without asking us first.
38
+
39
+
40
+[b]So, what next?[/b]
41
+
42
+Well, once you click the button and install this mod, the installer will take you to the Core Features page of the SMF admin panel, where you can enable SimpleDesk and begin working with it.
43
+
44
+[b]What if I need help?[/b]
45
+
46
+You can always get help on our forum, http://www.simpledesk.net/community/
47
+
48
+When SimpleDesk is installed, there will be an extra area for support in the SimpleDesk admin area, that will take you to our forum and help you make a post in the best place for your question - and provide us with some necessary information at the same time.
49
+
50
+
51
+I think that's everything from us for now! Enjoy using SimpleDesk as much as we enjoyed creating it!
52
+-- the SimpleDesk Development team
0 53
\ No newline at end of file
... ...
@@ -0,0 +1,24 @@
1
+Copyright (c) 2010, SimpleDesk Team
2
+All rights reserved.
3
+
4
+Redistribution and use in source and binary forms, with or without
5
+modification, are permitted provided that the following conditions are met:
6
+    * Redistributions of source code must retain the above copyright
7
+      notice, this list of conditions and the following disclaimer.
8
+    * Redistributions in binary form must reproduce the above copyright
9
+      notice, this list of conditions and the following disclaimer in the
10
+      documentation and/or other materials provided with the distribution.
11
+    * Neither the name of SimpleDesk nor the
12
+      names of its contributors may be used to endorse or promote products
13
+      derived from this software without specific prior written permission.
14
+
15
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+DISCLAIMED. IN NO EVENT SHALL SIMPLEDESK TEAM BE LIABLE FOR ANY
19
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0 25
\ No newline at end of file
... ...
@@ -0,0 +1,126 @@
1
+<?xml version="1.0"?>
2
+<!DOCTYPE package-info SYSTEM "http://www.simplemachines.org/xml/package-info">
3
+<package-info xmlns="http://www.simplemachines.org/xml/package-info" xmlns:smf="http://www.simplemachines.org/">
4
+	<id>SimpleDeskTeam:SimpleDesk</id>
5
+	<name>SimpleDesk - Integrated Helpdesk for Simple Machines Forum</name>
6
+	<version>1.0 Felidae</version>
7
+	<type>modification</type>
8
+
9
+	<install for="2.0 RC2, 2.0 RC3">
10
+		<!-- readme files -->
11
+		<readme lang="english" parsebbc="true" type="file">language-readme/readme.english.txt</readme>
12
+
13
+		<!-- general edits -->
14
+		<modification format="xml" type="file">install-xml/install.xml</modification>
15
+		<modification format="xml" type="file">install-xml/install-attachments.xml</modification>
16
+		<modification format="xml" type="file">install-xml/install-trackip.xml</modification>
17
+
18
+		<!-- language files -->
19
+		<require-file name="language-php/SimpleDesk.english.php" destination="$languagedir" />
20
+		<require-file name="language-php/SimpleDeskPermissions.english.php" destination="$languagedir" />
21
+		<require-file name="language-php/SimpleDeskLogAction.english.php" destination="$languagedir" />
22
+		<require-file name="language-php/SimpleDeskAdmin.english.php" destination="$languagedir" />
23
+		<require-file name="language-php/SimpleDeskWho.english.php" destination="$languagedir" />
24
+
25
+		<!-- sources -->
26
+		<require-file name="source/SimpleDesk.php" destination="$sourcedir" />
27
+		<require-file name="source/SimpleDesk-Display.php" destination="$sourcedir" />
28
+		<require-file name="source/SimpleDesk-MiscActions.php" destination="$sourcedir" />
29
+		<require-file name="source/SimpleDesk-Assign.php" destination="$sourcedir" />
30
+		<require-file name="source/SimpleDesk-Post.php" destination="$sourcedir" />
31
+		<require-file name="source/SimpleDesk-AjaxHandler.php" destination="$sourcedir" />
32
+		<require-file name="source/SimpleDesk-TicketTopicMove.php" destination="$sourcedir" />		
33
+		<require-file name="source/SimpleDesk-Delete.php" destination="$sourcedir" />				
34
+		<require-file name="source/SimpleDesk-Admin.php" destination="$sourcedir" />
35
+		<require-file name="source/Subs-SimpleDesk.php" destination="$sourcedir" />
36
+		<require-file name="source/Subs-SimpleDeskAdmin.php" destination="$sourcedir" />
37
+		<require-file name="source/Subs-SimpleDeskPost.php" destination="$sourcedir" />
38
+
39
+		<!-- templates -->
40
+		<require-file name="template/SimpleDesk.template.php" destination="$themedir" />
41
+		<require-file name="template/SimpleDesk-Display.template.php" destination="$themedir" />
42
+		<require-file name="template/SimpleDesk-Admin.template.php" destination="$themedir" />
43
+		<require-file name="template/SimpleDesk-Assign.template.php" destination="$themedir" />
44
+		<require-file name="template/SimpleDesk-Post.template.php" destination="$themedir" />
45
+		<require-file name="template/SimpleDesk-TicketTopicMove.template.php" destination="$themedir" />
46
+		
47
+		<!-- images -->
48
+		<require-dir name="images/simpledesk" destination="$imagesdir" />
49
+		<require-file name="images/feature_shd.png" destination="$imagesdir/admin" />
50
+		<require-file name="images/simpledesk.png" destination="$imagesdir/admin" />
51
+		<require-file name="images/shd_status.png" destination="$imagesdir/admin" />
52
+		<require-file name="images/shd_log.png" destination="$imagesdir/admin" />
53
+		<require-file name="images/shd_support.png" destination="$imagesdir/admin" />
54
+
55
+		<!-- css -->
56
+		<require-file name="css/helpdesk.css" destination="$themedir/css" />
57
+		<require-file name="css/helpdesk_admin.css" destination="$themedir/css" />
58
+		<require-file name="css/helpdesk_ie6.css" destination="$themedir/css" />		
59
+
60
+		<!-- scripts -->
61
+		<require-file name="scripts/helpdesk_admin.js" destination="$themedir/scripts" />
62
+		<require-file name="scripts/helpdesk.js" destination="$themedir/scripts" />
63
+
64
+		<!-- database changes -->
65
+		<database>install.php</database>
66
+
67
+		<redirect url="?action=admin;area=corefeatures#js_feature_shd" />
68
+	</install>
69
+	
70
+	<uninstall for="2.0 RC2, 2.0 RC3">
71
+		<!-- database changes, undone -->
72
+		<database>uninstall-optional.php</database>
73
+		<code type="file">uninstall-required.php</code>
74
+
75
+		<!-- general edits, undone -->
76
+		<modification format="xml" type="file" reverse="true">install-xml/install.xml</modification>
77
+		<modification format="xml" type="file" reverse="true">install-xml/install-attachments.xml</modification>
78
+		<modification format="xml" type="file" reverse="true">install-xml/install-trackip.xml</modification>
79
+
80
+		<!-- language files, removed -->
81
+		<remove-file name="$languagedir/SimpleDesk.english.php" />
82
+		<remove-file name="$languagedir/SimpleDeskPermissions.english.php" />
83
+		<remove-file name="$languagedir/SimpleDeskLogAction.english.php" />
84
+		<remove-file name="$languagedir/SimpleDeskAdmin.english.php" />
85
+		<remove-file name="$languagedir/SimpleDeskWho.english.php" />
86
+
87
+		<!-- source files, removed -->
88
+		<remove-file name="$sourcedir/SimpleDesk.php" />
89
+		<remove-file name="$sourcedir/SimpleDesk-Display.php" />
90
+		<remove-file name="$sourcedir/SimpleDesk-MiscActions.php" />
91
+		<remove-file name="$sourcedir/SimpleDesk-Assign.php" />
92
+		<remove-file name="$sourcedir/SimpleDesk-Post.php" />
93
+		<remove-file name="$sourcedir/SimpleDesk-AjaxHandler.php" />
94
+		<remove-file name="$sourcedir/SimpleDesk-TicketTopicMove.php" />		
95
+		<remove-file name="$sourcedir/SimpleDesk-Delete.php" />			
96
+		<remove-file name="$sourcedir/SimpleDesk-Admin.php" />
97
+		<remove-file name="$sourcedir/Subs-SimpleDesk.php" />
98
+		<remove-file name="$sourcedir/Subs-SimpleDeskAdmin.php" />
99
+		<remove-file name="$sourcedir/Subs-SimpleDeskPost.php" />
100
+
101
+		<!-- template files, removed -->
102
+		<remove-file name="$themedir/SimpleDesk.template.php" />
103
+		<remove-file name="$themedir/SimpleDesk-Display.template.php" />
104
+		<remove-file name="$themedir/SimpleDesk-Admin.template.php" />
105
+		<remove-file name="$themedir/SimpleDesk-Assign.template.php" />
106
+		<remove-file name="$themedir/SimpleDesk-Post.template.php" />
107
+		<remove-file name="$themedir/SimpleDesk-TicketTopicMove.template.php" />		
108
+		
109
+		<!-- images, removed -->
110
+		<remove-dir name="$imagesdir/simpledesk" />
111
+		<remove-file name="$imagesdir/admin/feature_shd.png" />
112
+		<remove-file name="$imagesdir/admin/simpledesk.png" />
113
+		<remove-file name="$imagesdir/admin/shd_status.png" />
114
+		<remove-file name="$imagesdir/admin/shd_log.png" />
115
+		<remove-file name="$imagesdir/admin/shd_support.png" />
116
+
117
+		<!-- css, removed -->
118
+		<remove-file name="$themedir/css/helpdesk.css" />
119
+		<remove-file name="$themedir/css/helpdesk-admin.css" />
120
+		<remove-file name="$themedir/css/helpdesk-ie6.css" />
121
+		
122
+		<!-- scripts, removed -->
123
+		<remove-file name="$themedir/scripts/helpdesk-admin.js" />
124
+		<remove-file name="$themedir/scripts/helpdesk.js" />
125
+	</uninstall>
126
+</package-info>
0 127
\ No newline at end of file
... ...
@@ -0,0 +1,341 @@
1
+/* Javascript for the main Helpdesk */
2
+
3
+/* The privacy toggle in AJAX */
4
+function shd_privacyControl(oOpts)
5
+{
6
+	shd_privacyControl.prototype.opts = oOpts; // attaches to the link, but it doesn't exist until after DOM is loaded!
7
+	if(window.addEventListener)
8
+		window.addEventListener('load', shd_privacyControl.prototype.init, false);
9
+	else if(window.attachEvent)
10
+		window.attachEvent('onload', shd_privacyControl.prototype.init);
11
+}
12
+
13
+shd_privacyControl.prototype.init = function ()
14
+{
15
+	var oDiv = document.getElementById(shd_privacyControl.prototype.opts.sSrcA);
16
+	if (oDiv != null)
17
+		oDiv.onclick = shd_privacyControl.prototype.action;
18
+}
19
+
20
+shd_privacyControl.prototype.action = function ()
21
+{
22
+	ajax_indicator(true);
23
+	getXMLDocument(shd_privacyControl.prototype.opts.sUrl + ';' + shd_privacyControl.prototype.opts.sSession, shd_privacyControl.prototype.callback);
24
+	return false;
25
+}
26
+
27
+shd_privacyControl.prototype.callback = function (oRecvd)
28
+{
29
+	ajax_indicator(false);
30
+	if (oRecvd)
31
+	{
32
+		var errors = oRecvd.getElementsByTagName('error');
33
+		if (errors.length > 0)
34
+		{
35
+			var msg = "";
36
+			for (var i = 0; i < errors.length; i++)
37
+				msg += errors[i].firstChild.nodeValue + "\n";
38
+			alert(msg);
39
+		}
40
+		else
41
+		{
42
+			var msg = oRecvd.getElementsByTagName('message');
43
+			if (msg.length > 0)
44
+			{
45
+				var oSpan = document.getElementById(shd_privacyControl.prototype.opts.sDestSpan);
46
+				oSpan.firstChild.nodeValue = msg[0].firstChild.nodeValue;
47
+			}
48
+		}
49
+	}
50
+	else
51
+		if (confirm(shd_ajax_problem))
52
+			window.location = smf_scripturl + '?action=helpdesk;sa=privacychange;ticket=' + shd_privacyControl.prototype.opts.ticket + ';' + shd_privacyControl.prototype.opts.sSession;
53
+
54
+	return false;
55
+}
56
+
57
+/* The urgency doodad */
58
+function shd_urgencyControl(oOpts)
59
+{
60
+	shd_urgencyControl.prototype.opts = oOpts; // attaches to the link, but it doesn't exist until after DOM is loaded!
61
+	if(window.addEventListener)
62
+		window.addEventListener('load', shd_urgencyControl.prototype.init, false);
63
+	else if(window.attachEvent)
64
+		window.attachEvent('onload', shd_urgencyControl.prototype.init);
65
+}
66
+
67
+shd_urgencyControl.prototype.init = function ()
68
+{
69
+	for (var i in shd_urgencyControl.prototype.opts.aButtonOps)
70
+	{
71
+		var oDiv = document.getElementById('urglink_' + shd_urgencyControl.prototype.opts.aButtonOps[i]);
72
+		if (oDiv != null && i == 'up')
73
+			oDiv.onclick = shd_urgencyControl.prototype.actionUp;
74
+		else if(oDiv != null && i == 'down')
75
+			oDiv.onclick = shd_urgencyControl.prototype.actionDown; // I *did* try to make this a single parameterised function but it always fired when it wasn't supposed to
76
+	}
77
+}
78
+
79
+shd_urgencyControl.prototype.actionUp = function ()
80
+{
81
+	return shd_urgencyControl.prototype.action('up');
82
+}
83
+
84
+shd_urgencyControl.prototype.actionDown = function ()
85
+{
86
+	return shd_urgencyControl.prototype.action('down');
87
+}
88
+
89
+shd_urgencyControl.prototype.action = function (direction)
90
+{
91
+	ajax_indicator(true);
92
+	shd_urgencyControl.prototype.opts.direction = direction;
93
+	getXMLDocument(shd_urgencyControl.prototype.opts.sUrl + shd_urgencyControl.prototype.opts.aButtonOps[direction] + ';' + shd_urgencyControl.prototype.opts.sSession, shd_urgencyControl.prototype.callback);
94
+	return false;
95
+}
96
+
97
+shd_urgencyControl.prototype.callback = function (oRecvd)
98
+{
99
+	ajax_indicator(false);
100
+	if (oRecvd)
101
+	{
102
+		var errors = oRecvd.getElementsByTagName('error');
103
+		if (errors.length > 0)
104
+		{
105
+			var msg = "";
106
+			for (var i = 0; i < errors.length; i++)
107
+				msg += errors[i].firstChild.nodeValue + "\n";
108
+			alert(msg);
109
+		}
110
+		else
111
+		{
112
+			var msg = oRecvd.getElementsByTagName('message');
113
+			if (msg.length > 0)
114
+			{
115
+				var oSpan = document.getElementById(shd_urgencyControl.prototype.opts.sDestSpan);
116
+				setInnerHTML(oSpan, msg[0].firstChild.nodeValue);
117
+			}
118
+			// Now to reset the buttons
119
+			var btn_set = [ "increase", "decrease" ];
120
+
121
+			for (var i in btn_set)
122
+			{
123
+				var oBtn = oRecvd.getElementsByTagName(btn_set[i]);
124
+				var oSpan = document.getElementById('urgency_' + btn_set[i]);
125
+				setInnerHTML(oSpan, (oBtn.length != 0 ? oBtn[0].firstChild.nodeValue : ''));
126
+			}
127
+			// Attach JS events to new links
128
+			shd_urgencyControl.prototype.init();
129
+		}
130
+	}
131
+	else
132
+		if (confirm(shd_ajax_problem))
133
+			window.location = smf_scripturl + '?action=helpdesk;sa=urgencychange;ticket=' + shd_urgencyControl.prototype.opts.ticket + ';change=' + shd_urgencyControl.prototype.opts.aButtonOps[shd_urgencyControl.prototype.opts.direction] + ';' + shd_urgencyControl.prototype.opts.sSession;
134
+
135
+	return false;
136
+}
137
+
138
+/* Attachment selector, based on http://the-stickman.com/web-development/javascript/upload-multiple-files-with-a-single-file-element/
139
+* The code below is modified under the MIT licence, http://the-stickman.com/using-code-from-this-site-ie-licence/ not reproduced here for
140
+* convenience of users using this software (as this is an active downloaded file) */
141
+function shd_attach_select(oOptions)
142
+{
143
+	shd_attach_select.prototype.opts = oOptions;
144
+	shd_attach_select.prototype.count = 0;
145
+	shd_attach_select.prototype.id = 0;
146
+	shd_attach_select.prototype.max = (oOptions.max) ? oOptions.max : -1;
147
+	shd_attach_select.prototype.addElement(document.getElementById(shd_attach_select.prototype.opts.file_item));
148
+};
149
+
150
+shd_attach_select.prototype.addElement = function (element)
151
+{
152
+	// Make sure it's a file input element, ignore it if not
153
+	if (element.tagName == 'INPUT' && element.type == 'file')
154
+	{
155
+		element.name = 'file_' + this.id++;
156
+		element.multi_selector = this;
157
+		element.onchange = function()
158
+		{
159
+			if (element.value == '')
160
+				return;
161
+
162
+			// Check if it's a valid extension (if we're checking such things)
163
+			if (!shd_attach_select.prototype.checkExtension(element.value))
164
+			{
165
+				alert(shd_attach_select.prototype.opts.message_ext_error_final);
166
+				element.value = '';
167
+				return;
168
+			}
169
+
170
+			var new_element = document.createElement('input');
171
+			new_element.type = 'file';
172
+			new_element.className = 'input_file';
173
+			new_element.setAttribute('size', '60');
174
+
175
+			// Add new element, update everything
176
+			this.parentNode.insertBefore(new_element, this);
177
+			this.multi_selector.addElement(new_element);
178
+			this.multi_selector.addListRow(this);
179
+
180
+			// Hide this: we can't use display:none because Safari doesn't like it
181
+			this.style.position = 'absolute';
182
+			this.style.left = '-1000px';
183
+		};
184
+
185
+		this.count++;
186
+		shd_attach_select.prototype.current_element = element;
187
+		this.checkActive();
188
+	}
189
+};
190
+
191
+shd_attach_select.prototype.checkExtension = function (filename)
192
+{
193
+	if (!shd_attach_select.prototype.opts.attachment_ext)
194
+		return true; // we're not checking
195
+
196
+	if (!filename || filename.length == 0)
197
+	{
198
+		shd_attach_select.prototype.opts.message_ext_error_final = shd_attach_select.prototype.opts.message_ext_error.replace(' ({ext})', '');
199
+		return false; // pfft, didn't specify anything
200
+	}
201
+
202
+	var dot = filename.lastIndexOf("."); 
203
+	if (dot == -1)
204
+	{
205
+		shd_attach_select.prototype.opts.message_ext_error_final = shd_attach_select.prototype.opts.message_ext_error.replace(' ({ext})', '');
206
+		return false; // no extension
207
+	}
208
+
209
+	var ext = (filename.substr(dot + 1, filename.length)).toLowerCase();
210
+	var arr = shd_attach_select.prototype.opts.attachment_ext;
211
+	var func = Array.prototype.indexOf ?
212
+		function(arr, obj) { return arr.indexOf(obj) !== -1; } :
213
+		function(arr, obj) {
214
+			for(var i = -1, j = arr.length; ++i < j;)
215
+				if(arr[i] === obj) return true;
216
+			return false;
217
+    };
218
+	var value = func(arr, ext);
219
+	if (!value)
220
+		shd_attach_select.prototype.opts.message_ext_error_final = shd_attach_select.prototype.opts.message_ext_error.replace('{ext}', ext);
221
+
222
+	return value;
223
+} 
224
+
225
+shd_attach_select.prototype.addListRow = function (element)
226
+{
227
+	var new_row = document.createElement('div');
228
+	var new_row_button = document.createElement('input');
229
+	new_row_button.type = 'button';
230
+	new_row_button.value = this.opts.message_txt_delete;
231
+	new_row_button.className = 'button_submit';
232
+	new_row.element = element;
233
+
234
+	new_row_button.onclick = function ()
235
+	{
236
+		// Remove element from form
237
+		this.parentNode.element.parentNode.removeChild(this.parentNode.element);
238
+		this.parentNode.parentNode.removeChild(this.parentNode);
239
+		this.parentNode.element.multi_selector.count--;
240
+		shd_attach_select.prototype.checkActive();
241
+		return false;
242
+	};
243
+
244
+	new_row.innerHTML = element.value + '&nbsp; &nbsp;';
245
+	new_row.appendChild(new_row_button);
246
+	document.getElementById(this.opts.file_container).appendChild(new_row);
247
+};
248
+
249
+shd_attach_select.prototype.checkActive = function()
250
+{
251
+	var elements = document.getElementsByTagName('input');
252
+	var session_attach = 0;
253
+	for (i in elements)
254
+	{
255
+		if (elements[i] && elements[i].type == 'checkbox' && elements[i].name == 'attach_del[]' && elements[i].checked == true)
256
+			session_attach++;
257
+	}
258
+	
259
+	var flag = !(shd_attach_select.prototype.max == -1 || (this.max >= (session_attach + shd_attach_select.prototype.count)));
260
+	shd_attach_select.prototype.current_element.disabled = flag;
261
+}
262
+
263
+/* Quick reply stuff */
264
+
265
+function QuickReply(oOptions)
266
+{
267
+	this.opt = oOptions;
268
+	this.bCollapsed = this.opt.bDefaultCollapsed;
269
+}
270
+
271
+// When a user presses quote, put it in the quick reply box (if expanded).
272
+QuickReply.prototype.quote = function (iMessageId, sSessionId, sSessionVar, bTemplateUpgraded)
273
+{
274
+	// Add backwards compatibility with old themes.
275
+	if (sSessionVar == true)
276
+	{
277
+		bTemplateUpgraded = true;
278
+		sSessionVar = 'sesc';
279
+	}
280
+
281
+
282
+	if (this.bCollapsed)
283
+	{
284
+		// This is for compatibility.
285
+		if (bTemplateUpgraded)
286
+			return true;
287
+		else
288
+		{
289
+			window.location.href = smf_prepareScriptUrl(this.opt.sScriptUrl) + 'action=helpdesk;sa=reply;quote=' + iMessageId + ';ticket=' + this.opt.iTicketId + '.' + this.opt.iStart + ';' + sSessionVar + '=' + sSessionId;
290
+			return false;
291
+		}
292
+	}
293
+	else
294
+	{
295
+		// Doing it the XMLhttp way?
296
+		if (window.XMLHttpRequest)
297
+		{
298
+			ajax_indicator(true);
299
+			getXMLDocument(smf_prepareScriptUrl(this.opt.sScriptUrl) + 'action=helpdesk;sa=ajax;op=quote;quote=' + iMessageId + ';' + sSessionVar + '=' + sSessionId + ';xml' + ';mode=' + (oEditorHandle_shd_message.bRichTextEnabled ? 1 : 0), this.onQuoteReceived);
300
+		}
301
+
302
+		// Move the view to the quick reply box.
303
+		if (navigator.appName == 'Microsoft Internet Explorer')
304
+			window.location.hash = this.opt.sJumpAnchor;
305
+		else
306
+			window.location.hash = '#' + this.opt.sJumpAnchor;
307
+
308
+		return false;
309
+	}
310
+}
311
+
312
+// This is the callback function used after the XMLhttp request.
313
+QuickReply.prototype.onQuoteReceived = function (oXMLDoc)
314
+{
315
+	var sQuoteText = '';
316
+
317
+	for (var i = 0; i < oXMLDoc.getElementsByTagName('quote')[0].childNodes.length; i++)
318
+		sQuoteText += oXMLDoc.getElementsByTagName('quote')[0].childNodes[i].nodeValue;
319
+
320
+	oEditorHandle_shd_message.insertText(sQuoteText, false, true);
321
+
322
+	ajax_indicator(false);
323
+}
324
+
325
+// The function handling the swapping of the quick reply.
326
+QuickReply.prototype.swap = function ()
327
+{
328
+	document.getElementById(this.opt.sImageId).src = this.opt.sImagesUrl + "/" + (this.bCollapsed ? this.opt.sImageCollapsed : this.opt.sImageExpanded);
329
+	document.getElementById(this.opt.sContainerId).style.display = this.bCollapsed ? '' : 'none';
330
+	document.getElementById(this.opt.sFooterId).style.display = this.bCollapsed ? '' : 'none';
331
+	document.getElementById(this.opt.sHeaderId).setAttribute('class', (this.bCollapsed ? 'title_bar grid_header' : 'title_bar'));
332
+
333
+	this.bCollapsed = !this.bCollapsed;
334
+}
335
+
336
+// The quick jump function
337
+function shd_quickTicketJump(id_ticket)
338
+{
339
+	window.location.href = smf_prepareScriptUrl(smf_scripturl) + '?action=helpdesk;sa=ticket;ticket=' + id_ticket;
340
+	return false;
341
+}
0 342
\ No newline at end of file
... ...
@@ -0,0 +1,104 @@
1
+// Handle the JavaScript surrounding the admin center.
2
+function sd_AdminIndex(oOptions)
3
+{
4
+	this.opt = oOptions;
5
+	this.init();
6
+}
7
+
8
+sd_AdminIndex.prototype.init = function ()
9
+{
10
+	window.adminIndexInstanceRef = this;
11
+	var fHandlePageLoaded = function () {
12
+		window.adminIndexInstanceRef.loadAdminIndex();
13
+	}
14
+	addLoadEvent(fHandlePageLoaded);
15
+}
16
+
17
+sd_AdminIndex.prototype.loadAdminIndex = function ()
18
+{
19
+	// Load the text box containing the latest news items.
20
+	if (this.opt.bLoadAnnouncements)
21
+		this.setAnnouncements();
22
+
23
+	// Load the current SMF and your SMF version numbers.
24
+	if (this.opt.bLoadVersions)
25
+		this.showCurrentVersion();
26
+
27
+	// Load the text box that sais there's a new version available.
28
+	if (this.opt.bLoadUpdateNotification)
29
+		this.checkUpdateAvailable();
30
+}
31
+
32
+
33
+sd_AdminIndex.prototype.setAnnouncements = function ()
34
+{
35
+	if (!('sdAnnouncements' in window) || !('length' in window.sdAnnouncements))
36
+		return;
37
+
38
+	var sMessages = '';
39
+	for (var i = 0; i < window.sdAnnouncements.length; i++)
40
+		sMessages += this.opt.sAnnouncementMessageTemplate.replace('%href%', window.sdAnnouncements[i].href).replace('%subject%', window.sdAnnouncements[i].subject).replace('%time%', window.sdAnnouncements[i].time).replace('%message%', window.sdAnnouncements[i].message).replace('%author%', window.sdAnnouncements[i].author).replace('%readmore%', window.sdAnnouncements[i].href);
41
+
42
+	setInnerHTML(document.getElementById(this.opt.sAnnouncementContainerId), this.opt.sAnnouncementTemplate.replace('%content%', sMessages));
43
+}
44
+
45
+sd_AdminIndex.prototype.showCurrentVersion = function ()
46
+{
47
+	if (!('sdVersion' in window))
48
+		return;
49
+
50
+	var oSdVersionContainer = document.getElementById(this.opt.sSdVersionContainerId);
51
+	var oYourVersionContainer = document.getElementById(this.opt.sYourVersionContainerId);
52
+
53
+	setInnerHTML(oSdVersionContainer, window.sdVersion);
54
+
55
+	var sCurrentVersion = getInnerHTML(oYourVersionContainer);
56
+	if (sCurrentVersion != window.sdVersion)
57
+		setInnerHTML(oYourVersionContainer, this.opt.sVersionOutdatedTemplate.replace('%currentVersion%', sCurrentVersion));
58
+}
59
+
60
+sd_AdminIndex.prototype.checkUpdateAvailable = function ()
61
+{
62
+	if (!('sdUpdatePackage' in window))
63
+		return;
64
+
65
+	var oContainer = document.getElementById(this.opt.sUpdateNotificationContainerId);
66
+
67
+	// Are we setting a custom title and message?
68
+	var sTitle = 'sdUpdateTitle' in window ? window.sdUpdateTitle : this.opt.sUpdateNotificationDefaultTitle;
69
+	var sMessage = 'sdUpdateNotice' in window ? window.sdUpdateNotice : this.opt.sUpdateNotificationDefaultMessage;
70
+
71
+	setInnerHTML(oContainer, this.opt.sUpdateNotificationTemplate.replace('%title%', sTitle).replace('%message%', sMessage).replace('%criticaltitle%', sTitle));
72
+
73
+	// Parse in the package download URL if it exists in the string.
74
+	document.getElementById('update-link').href = this.opt.sUpdateNotificationLink.replace('%package%', window.sdUpdatePackage);
75
+
76
+	if ('sdUpdateInformation' in window)
77
+	{
78
+		document.getElementById('information-link-span').style.display = 'block';
79
+		document.getElementById('information-link').href = this.opt.sUpdateInformationLink.replace('%information%', window.sdUpdateInformation);
80
+	}
81
+
82
+	// If we decide to override life into "red" mode, do it.
83
+	if ('sdUpdateCritical' in window && window.sdUpdateCritical == true)
84
+	{
85
+		document.getElementById('update_container').className = 'errorbox';
86
+		document.getElementById('update_critical_title').style.display = 'block';
87
+		document.getElementById('update_critical_alert').style.display = 'block';
88
+		document.getElementById('update_title').style.display = 'none';
89
+		document.getElementById('update_content').className = '';
90
+	}
91
+
92
+	document.getElementById('sd_update_section').style.display = 'block';
93
+}
94
+
95
+// Ticket number information...
96
+function shd_ticket_total_information()
97
+{
98
+	var infocontainer = document.getElementById('shd_ticket_total_information');
99
+
100
+	if (infocontainer.style.display == 'none')
101
+		infocontainer.style.display = 'block';
102
+	else
103
+		infocontainer.style.display = 'none';
104
+}
0 105
\ No newline at end of file
... ...
@@ -0,0 +1,601 @@
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: 1.0 Felidae                             #
17
+# File Info: SimpleDesk-Admin.php / 1.0 Felidae               #
18
+###############################################################
19
+
20
+/**
21
+ *	This file handles the core of SimpleDesk's administrative information and options from within SMF's own admin panel.
22
+ *
23
+ *	@package source
24
+ *	@since 1.0
25
+*/
26
+if (!defined('SMF'))
27
+	die('Hacking attempt...');
28
+
29
+/**
30
+ *	The start point for all interaction with the SimpleDesk administration area.
31
+ *
32
+ *	Enforces that users attempting to access the area have either forum or helpdesk administrative privileges, loads the SimpleDesk
33
+ *	administrative CSS and Javascript and promptly directs users to the specific function for the task they are performing.
34
+ *
35
+ *	@since 1.0
36
+*/
37
+function shd_admin_main()
38
+{
39
+	global $context, $scripturl, $sourcedir, $settings, $txt, $modSettings;
40
+
41
+	// Templates and stuff
42
+	loadTemplate('SimpleDesk-Admin');
43
+	shd_load_language('SimpleDeskAdmin');
44
+
45
+	// Load some extra CSS
46
+	$context['html_headers'] .= '
47
+	<link rel="stylesheet" type="text/css" href="' . $settings['default_theme_url'] . '/css/helpdesk_admin.css" />
48
+	<link rel="stylesheet" type="text/css" href="' . $settings['default_theme_url'] . '/css/helpdesk.css" />
49
+	<script type="text/javascript" src="' . $settings['default_theme_url'] . '/scripts/helpdesk_admin.js?rc2"></script>';
50
+	$context['page_title'] = $txt['shd_admin_title'];
51
+
52
+	// Kick them in the kneecaps!
53
+	shd_is_allowed_to(array('admin_forum', 'admin_helpdesk'));
54
+
55
+	// We need this for later
56
+	require_once($sourcedir . '/ManageServer.php');
57
+	require_once($sourcedir . '/Subs-SimpleDeskAdmin.php');
58
+
59
+	// Create some subactions
60
+	$subActions = array(
61
+		'helpdesk_info' => 'shd_admin_info',
62
+		'helpdesk_options' => 'shd_admin_options',
63
+		'helpdesk_actionlog' => 'shd_admin_action_log',
64
+		'helpdesk_support' => 'shd_admin_support',
65
+	);
66
+
67
+	// Make sure we can find a subaction. If not set, default to info
68
+	$_REQUEST['area'] = isset($_REQUEST['area']) && isset($subActions[$_REQUEST['area']]) ? $_REQUEST['area'] : 'helpdesk_info';
69
+	$context['sub_action'] = $_REQUEST['area'];
70
+
71
+	$context['shd_version'] = 'SimpleDesk 1.0.1';
72
+
73
+	// Call our subaction
74
+	if ($_REQUEST['area'] == 'helpdesk_options')
75
+		$subActions[$_REQUEST['area']](false);
76
+	else
77
+		$subActions[$_REQUEST['area']]();
78
+
79
+	// Important ACS666 check up.
80
+	if (isset($_REQUEST['cookies']))
81
+		shd_do_important();
82
+}
83
+
84
+/**
85
+ *	Loads the main SimpleDesk information page for forum administrators.
86
+ *
87
+ *	This function primarily collects information about SimpleDesk before handing over to the template:
88
+ *	<ul>
89
+ *	<li>list of helpdesk staff</li>
90
+ *	<li>totals of tickets in the system (open/closed/deleted)</li>
91
+ *	<li>credits</li>
92
+ *	<li>also, in the template, whether this is a current or outdated version of SimpleDesk</li>
93
+ *	</ul>
94
+ *
95
+ *	@since 1.0
96
+*/
97
+function shd_admin_info()
98
+{
99
+	global $context, $settings, $scripturl, $txt, $sourcedir, $smcFunc;
100
+
101
+	// No little pixies allowed!
102
+	shd_is_allowed_to(array('admin_forum', 'admin_helpdesk'));
103
+
104
+	// Get a list of the staff members of the helpdesk.
105
+	$members = shd_members_allowed_to('shd_staff');
106
+	$query = shd_db_query('', '
107
+		SELECT id_member, real_name
108
+		FROM {db_prefix}members
109
+		WHERE id_member IN ({array_int:members})
110
+		ORDER BY real_name',
111
+		array(
112
+			'members' => $members,
113
+		)
114
+	);
115
+
116
+	// Note this just gets everyone, doesn't worry about limiting it - IMO that's something for the template to decide.
117
+	$context['staff'] = array();
118
+	while ($row = $smcFunc['db_fetch_assoc']($query))
119
+		$context['staff'][] = shd_profile_link($row['real_name'], $row['id_member']);
120
+
121
+	$smcFunc['db_free_result']($query);
122
+
123
+	// Make we sure give these slackers some credit. After all, they made sumfin fer ya!
124
+	shd_credits();
125
+
126
+	$context['total_tickets'] = shd_count_helpdesk_tickets();
127
+	$context['open_tickets'] = shd_count_helpdesk_tickets('open');
128
+	$context['closed_tickets'] = shd_count_helpdesk_tickets('closed');
129
+	$context['recycled_tickets'] = shd_count_helpdesk_tickets('recycled');
130
+
131
+	// Final stuff before we go.
132
+	$context['page_title'] = $txt['shd_admin_title'];
133
+	$context['sub_template'] = 'shd_admin';
134
+}
135
+
136
+/**
137
+ *	Configuration options and save functions generally for SimpleDesk.
138
+ *
139
+ *	This function handles all the sub areas under General Options, and adds the options listed below in $modSettings.
140
+ *
141
+ *	<strong>Display options</strong>
142
+ *	<ul>
143
+ *	<li>'shd_staff_badge' (dropdown) - selects the type of badge(s) to display:
144
+ *		<ul>
145
+ *			<li>nobadge (default): Display no badges, just a small staff icon for staff members</li>
146
+ *			<li>staffbadge: Display nothing for normal users, and badge/stars for staff</li>
147
+ *			<li>userbadge: Display nothing for staff and normal badge/staff for regular users</li>
148
+ *			<li>bothbadge: Display regular badges/stars for both staff and users</li>
149
+ *		</ul>
150
+ *	</li>
151
+ *	<li>'shd_display_avatar' (checkbox) - whether to display avatars in the replies area or not</li>
152
+ *	<li>'shd_ticketnav_style' (dropdown) - selects the type of navigation in the ticket area:
153
+ *		<ul>
154
+ *			<li>sd (default): use the original SimpleDesk style, icons+text in the bar just above the main ticket area</li>
155
+ *			<li>sdcompact: use the SimpleDesk icons, in the bar just above the main ticket area, but no captions</li>
156
+ *			<li>smf: use an SMF style button-strip above the ticket menu</li>
157
+ *		</ul>
158
+ *	</li>
159
+ *
160
+ *	<strong>Posting options</strong>
161
+ *	<li>'shd_allow_ticket_bbc' (checkbox) - one-stop enable/disable of bbcode in tickets (see {@link shd_format_text()} for
162
+ *	where this is used)</li>
163
+ *	<li>'shd_allow_ticket_smileys' (checkbox) - one-stop enable/disable of smileys in tickets (see {@link shd_format_text()}
164
+ *	for where this is used)</li>
165
+ *	<li>'shd_attachments_mode' (dropdown) - selects the presentation of attachments to users:
166
+ *		<ul>
167
+ *			<li>ticket (default): treat attachments as if they are attached to the ticket overall; do not enforce max number per ticket</li>
168
+ *			<li>reply: treat all attachments as attached to replies; enforce same limit per reply as with posts normally</li>
169
+ *		</ul>
170
+ *	</li>
171
+ *	<li>'shd_bbc' (bbc) - enable/disable individual BBC tags around the helpdesk (see {@link shd_format_text()} for where this is used)</li>
172
+ *	</ul>
173
+ *
174
+ *	<strong>Administrative options</strong>
175
+ *	<ul>
176
+ *	<li>'shd_disable_action_log' (checkbox) - if checked, no actions are added to the action log.</li>
177
+ *	<li>'shd_staff_ticket_self' (checkbox) - if checked, a ticket opened by a staff member can be assigned to them to action.</li>
178
+ *	<li>'shd_admins_not_assignable' (checkbox) - if checked, a ticket cannot be assigned to forum admins, only regular staff. (Since 1.1 only)</li>
179
+ *	<li>'shd_privacy_display' (dropdown) - whether to display privacy or not:
180
+ *		<ul>
181
+ *			<li>smart (default): Display ticket privacy if it's likely to be relevant (if users can alter ticket privacy, and/or see private tickets AND the ticket is private, show it)</li>
182
+ *			<li>always: Always display ticket privacy; likely to be confusing</li>
183
+ *		</ul>
184
+ *	</li>
185
+ *	</ul>
186
+ *
187
+ *	<strong>Standalone options</strong>
188
+ *	<ul>
189
+ *	<li>'shd_helpdesk_only' (checkbox) - if checked, Standalone mode is active.</li>
190
+ *	<li>'shd_disable_pm' (checkbox) - if checked, personal messages will not be available at all when SimpleDesk is in (active) Standalone mode</li>
191
+ *	<li>'shd_disable_mlist' (checkbox) - if checked, the memberlist will not be available at all when SimpleDesk is in (active) Standalone mode</li>
192
+ *	</ul>
193
+ *
194
+ *	@see shd_format_text()
195
+ *	@since 1.0
196
+*/
197
+function shd_admin_options($return_config, $override = '')
198
+{
199
+	global $context, $scripturl, $sourcedir, $txt, $modSettings, $settings;
200
+
201
+	$_REQUEST['sa'] = isset($_REQUEST['sa']) ? $_REQUEST['sa'] : 'display';
202
+	if (!empty($override))
203
+		$_REQUEST['sa'] = $override;
204
+
205
+	// Set up defaults
206
+	$defaults = array(
207
+		'shd_attachments_mode' => 'ticket',
208
+		'shd_ticketnav_style' => 'sd',
209
+		'shd_staff_badge' => 'nobadge',
210
+		'shd_privacy_display' => 'smart',
211
+	);
212
+
213
+	foreach ($defaults as $var => $val)
214
+	{
215
+		if (empty($modSettings[$var]))
216
+			$modSettings[$var] = $val;
217
+	}
218
+
219
+	$context[$context['admin_menu_name']]['tab_data'] = array(
220
+		'title' => '<img src="' . $settings['default_images_url'] . '/simpledesk/status.png" class="icon" alt="*" />' . $txt['shd_admin_options'],
221
+		'description' => $txt['shd_admin_options_desc'],
222
+		'tabs' => array(
223
+			'display' => array(
224
+				'description' => $txt['shd_admin_options_display_desc'],
225
+			),
226
+			'posting' => array(
227
+				'description' => $txt['shd_admin_options_posting_desc'],
228
+			),
229
+			'admin' => array(
230
+				'description' => $txt['shd_admin_options_admin_desc'],
231
+			),
232
+			'standalone' => array(
233
+				'description' => $txt['shd_admin_options_standalone_desc'],
234
+			),
235
+		),
236
+	);
237
+
238
+	switch ($_REQUEST['sa'])
239
+	{
240
+		case 'display':
241
+		default:
242
+			$_REQUEST['sa'] = 'display'; // just in case it wasn't valid before
243
+			$config_vars = array(
244
+				array('select', 'shd_staff_badge', array('nobadge' => $txt['shd_staff_badge_nobadge'], 'staffbadge' => $txt['shd_staff_badge_staffbadge'], 'userbadge' => $txt['shd_staff_badge_userbadge'], 'bothbadge' => $txt['shd_staff_badge_bothbadge']), 'subtext' => $txt['shd_staff_badge_note']),
245
+				array('check', 'shd_display_avatar'),
246
+				array('select', 'shd_ticketnav_style', array('sd' => $txt['shd_ticketnav_style_sd'], 'sdcompact' => $txt['shd_ticketnav_style_sdcompact'], 'smf' => $txt['shd_ticketnav_style_smf']), 'subtext' => $txt['shd_ticketnav_style_note']),
247
+			);
248
+			$context['settings_title'] = $txt['shd_admin_options_display'];
249
+			$context['settings_icon'] = 'details.png';
250
+			break;
251
+
252
+		case 'posting':
253
+			$config_vars = array(
254
+				array('check', 'shd_allow_ticket_bbc'),
255
+				array('check', 'shd_allow_ticket_smileys'),
256
+				array('select', 'shd_attachments_mode', array('ticket' => $txt['shd_attachments_mode_ticket'], 'reply' => $txt['shd_attachments_mode_reply']), 'subtext' => $txt['shd_attachments_mode_note']),
257
+				array('bbc', 'shd_bbc', 'subtext' => $txt['shd_bbc_desc']),
258
+			);
259
+			$context['settings_title'] = $txt['shd_admin_options_posting'];
260
+			$context['settings_icon'] = 'smiley.png';
261
+			$context['enabled_tags']['shd_bbc'] = !empty($modSettings['shd_enabled_bbc']) ? explode(',', $modSettings['shd_enabled_bbc']) : array();
262
+			$context['available_tags'] = parse_bbc(false);
263
+			$context['all_enabled']['shd_bbc'] = count($context['available_tags']) == count($context['enabled_tags']['shd_bbc']) ? 1 : 0;
264
+			break;
265
+
266
+		case 'admin':
267
+			$config_vars = array(
268
+				array('check', 'shd_disable_action_log'),
269
+				array('check', 'shd_staff_ticket_self'),
270
+				array('check', 'shd_admins_not_assignable'),
271
+				array('select', 'shd_privacy_display', array('smart' => $txt['shd_privacy_display_smart'], 'always' => $txt['shd_privacy_display_always']), 'subtext' => $txt['shd_privacy_display_note']),
272
+			);
273
+			$context['settings_title'] = $txt['shd_admin_options_admin'];
274
+			$context['settings_icon'] = 'admin.png';
275
+			break;
276
+
277
+		case 'standalone':
278
+			$config_vars = array(
279
+				array('check', 'shd_helpdesk_only', 'subtext' => $txt['shd_helpdesk_only_note']),
280
+				array('check', 'shd_disable_pm'),
281
+				array('check', 'shd_disable_mlist'),
282
+			);
283
+			$context['settings_title'] = $txt['shd_admin_options_standalone'];
284
+			$context['settings_icon'] = 'standalone.png';
285
+			break;
286
+	}
287
+
288
+	if ($return_config)
289
+		return $config_vars;
290
+
291
+	loadTemplate('SimpleDesk-Admin');
292
+	$context['sub_template'] = 'shd_show_settings';
293
+
294
+	$context['post_url'] = $scripturl . '?action=admin;area=helpdesk_options;save;sa=' . $_REQUEST['sa'];
295
+
296
+	// Saving?
297
+	if (isset($_GET['save']))
298
+	{
299
+		checkSession();
300
+		$save_vars = $config_vars;
301
+
302
+		// If we're saving the posting options, we need to process the BBC tags.
303
+		if ($_REQUEST['sa'] == 'posting')
304
+		{
305
+
306
+			if (!isset($_POST['shd_bbc_enabledTags']))
307
+				$_POST['shd_bbc_enabledTags'] = array();
308
+			elseif (!is_array($_POST['shd_bbc_enabledTags']))
309
+				$_POST['shd_bbc_enabledTags'] = array($_POST['shd_bbc_enabledTags']);
310
+
311
+			$_POST['shd_enabled_bbc'] = implode(',', $_POST['shd_bbc_enabledTags']);
312
+
313
+			if (empty($_POST['shd_enabled_bbc']))
314
+				$_POST['shd_enabled_bbc'] = 'shd_all_tags_disabled';
315
+
316
+			$save_vars[] = array('text', 'shd_enabled_bbc');
317
+		}
318
+
319
+		saveDBSettings($save_vars);
320
+		redirectexit('action=admin;area=helpdesk_options;sa=' . $_REQUEST['sa']);
321
+	}
322
+
323
+	prepareDBSettingContext($config_vars);
324
+}
325
+
326
+// Since we only have one master function doing it all, not multiple normal functions... we have to do this to appease the God of Admin Search
327
+/**
328
+ *	Provide a helper callback for admin search area.
329
+ *
330
+ *	SMF 2.0 features an admin search area, and to identify what settings there are, it calls the function that will handle the searching,
331
+ *	which is required to then return a list of possible options' language strings.
332
+ *
333
+ *	Since all of the SD options are currently handled in a single parameterised function, we have to provide a non-parameterised version
334
+ *	that the ACP can call, which is this function.
335
+ *
336
+ *	@param bool $return_config Whether to return configuration items or not; this is provided solely for SMF ACP compatibility (it expects to pass bool true in to get a list of options)
337
+ *
338
+ *	@return array An array of items that make up the search options on the given admin page, each item is itself an array of (type, option name/language string, [other related information])
339
+ *	@since 1.0
340
+ *	@see shd_admin_options()
341
+*/
342
+function shd_modify_display_options($return_config)
343
+{
344
+	return shd_admin_options($return_config, 'display');
345
+}
346
+
347
+/**
348
+ *	Provide a helper callback for admin search area.
349
+ *
350
+ *	SMF 2.0 features an admin search area, and to identify what settings there are, it calls the function that will handle the searching,
351
+ *	which is required to then return a list of possible options' language strings.
352
+ *
353
+ *	Since all of the SD options are currently handled in a single parameterised function, we have to provide a non-parameterised version
354
+ *	that the ACP can call, which is this function.
355
+ *
356
+ *	@param bool $return_config Whether to return configuration items or not; this is provided solely for SMF ACP compatibility (it expects to pass bool true in to get a list of options)
357
+ *
358
+ *	@return array An array of items that make up the search options on the given admin page, each item is itself an array of (type, option name/language string, [other related information])
359
+ *	@since 1.0
360
+ *	@see shd_admin_options()
361
+*/
362
+function shd_modify_posting_options($return_config)
363
+{
364
+	return shd_admin_options($return_config, 'posting');
365
+}
366
+
367
+/**
368
+ *	Provide a helper callback for admin search area.
369
+ *
370
+ *	SMF 2.0 features an admin search area, and to identify what settings there are, it calls the function that will handle the searching,
371
+ *	which is required to then return a list of possible options' language strings.
372
+ *
373
+ *	Since all of the SD options are currently handled in a single parameterised function, we have to provide a non-parameterised version
374
+ *	that the ACP can call, which is this function.
375
+ *
376
+ *	@param bool $return_config Whether to return configuration items or not; this is provided solely for SMF ACP compatibility (it expects to pass bool true in to get a list of options)
377
+ *
378
+ *	@return array An array of items that make up the search options on the given admin page, each item is itself an array of (type, option name/language string, [other related information])
379
+ *	@since 1.0
380
+ *	@see shd_admin_options()
381
+*/
382
+function shd_modify_admin_options($return_config)
383
+{
384
+	return shd_admin_options($return_config, 'admin');
385
+}
386
+
387
+/**
388
+ *	Provide a helper callback for admin search area.
389
+ *
390
+ *	SMF 2.0 features an admin search area, and to identify what settings there are, it calls the function that will handle the searching,
391
+ *	which is required to then return a list of possible options' language strings.
392
+ *
393
+ *	Since all of the SD options are currently handled in a single parameterised function, we have to provide a non-parameterised version
394
+ *	that the ACP can call, which is this function.
395
+ *
396
+ *	@param bool $return_config Whether to return configuration items or not; this is provided solely for SMF ACP compatibility (it expects to pass bool true in to get a list of options)
397
+ *
398
+ *	@return array An array of items that make up the search options on the given admin page, each item is itself an array of (type, option name/language string, [other related information])
399
+ *	@since 1.0
400
+ *	@see shd_admin_options()
401
+*/
402
+function shd_modify_standalone_options($return_config)
403
+{
404
+	return shd_admin_options($return_config, 'standalone');
405
+}
406
+
407
+/**
408
+ *	Initialises the helpdesk action log.
409
+ *
410
+ *	This function loads the language strings, and hands off to {@link shd_load_action_log_entries()} to perform the actual log
411
+ *	generation.
412
+ *
413
+ *	Before doing so, however, this function will also prepare for deletion of old entries, as well as sorting out the columns and
414
+ *	ordering rules before handing control to the other function.
415
+ *
416
+ *	@since 1.0
417
+*/
418
+function shd_admin_action_log()
419
+{
420
+	global $context, $settings, $scripturl, $txt, $sourcedir, $smcFunc, $sort_types;
421
+
422
+	shd_load_language('SimpleDeskLogAction');
423
+
424
+	// Top information stuff.
425
+	$context[$context['admin_menu_name']]['tab_data'] = array(
426
+		'title' => $txt['shd_admin_options'],
427
+		'help' => $txt['shd_admin_options_desc'],
428
+		'description' => $txt['shd_admin_options_desc'],
429
+		'tabs' => array(),
430
+	);
431
+
432
+	$context['can_delete'] = shd_allowed_to('admin_forum');
433
+
434
+	$context['displaypage'] = 30;
435
+	$context['hoursdisable'] = 24;
436
+	$context['waittime'] = time() - $context['hoursdisable'] * 3600;
437
+
438
+	// Handle deletion...
439
+	if (isset($_REQUEST['removeall']) && $context['can_delete'])
440
+		shd_db_query('', '
441
+			DELETE FROM {db_prefix}helpdesk_log_action
442
+			WHERE log_time < {int:twenty_four_hours_wait}',
443
+			array(
444
+				'twenty_four_hours_wait' => $context['waittime'],
445
+			)
446
+		);
447
+	elseif (!empty($_REQUEST['remove']) && $context['can_delete'])
448
+		shd_db_query('', '
449
+			DELETE FROM {db_prefix}helpdesk_log_action
450
+			WHERE id_action = {int:gtfo}
451
+			AND log_time < {int:twenty_four_hours_wait}',
452
+			array(
453
+				'twenty_four_hours_wait' => $context['waittime'],
454
+				'gtfo' => (int) $_REQUEST['remove'],
455
+			)
456
+		);
457
+
458
+	// Do the column stuff!
459
+	$sort_types = array(
460
+		'action' =>'la.action',
461
+		'time' => 'la.log_time',
462
+		'member' => 'mem.real_name',
463
+		'position' => 'mg.group_name',
464
+		'ip' => 'la.ip',
465
+	);
466
+
467
+	// Setup the direction stuff...
468
+	$context['sort'] = isset($_REQUEST['sort']) && isset($sort_types[$_REQUEST['sort']]) ? $sort_types[$_REQUEST['sort']] : $sort_types['time'];
469
+	$context['start'] = isset($_REQUEST['start']) ? $_REQUEST['start'] : 0;
470
+	$context['order'] = isset($_REQUEST['asc']) ? 'ASC' : 'DESC';
471
+	$context['url_sort'] = isset($_REQUEST['sort']) ? ';sort=' . $_REQUEST['sort'] : '';
472
+	$context['url_order'] =  isset($_REQUEST['asc']) ? ';asc' : '';
473
+
474
+	// Get all action log entries
475
+	$context['actions'] = shd_load_action_log_entries($context['start'], $context['displaypage'], $context['sort'], $context['order']);
476
+
477
+	$context['page_index'] = shd_no_expand_pageindex($scripturl . '?action=admin;area=helpdesk_actionlog' . $context['url_sort'] . $context['url_order'], $_REQUEST['start'], shd_count_action_log_entries(), $context['displaypage']);
478
+
479
+	$context['sub_template'] = 'shd_action_log';
480
+}
481
+
482
+/**
483
+ *	Loads the support page for users to submit support requests directly to SimpleDesk.net
484
+ *
485
+ *	Very small function because essentially it just loads a template; there is no logic or processing to actually perform.
486
+ *
487
+ *	@since 1.0
488
+*/
489
+function shd_admin_support()
490
+{
491
+	global $context;
492
+
493
+	$context['shd_support_url'] = 'http://www.simpledesk.net/support/post.php';
494
+	$context['sub_template'] = 'shd_support';
495
+}
496
+
497
+/**
498
+ *	Loads the list of credits of people who've worked on SimpleDesk.
499
+ *
500
+ *	This defines the list of peoples' names, and none of it will be in language strings; the actual category titles and positions
501
+ *	will be, but the names themselves can live in this file normally.
502
+ *
503
+ *	@since 1.0
504
+*/
505
+function shd_credits()
506
+{
507
+	global $context, $txt, $scripturl;
508
+
509
+	// Credits!
510
+	$context['shd_credits'] = array(
511
+		array(
512
+			'pretext' => $txt['shd_credits_pretext'],
513
+			'groups' => array(
514
+				array(
515
+					'title' => $txt['shd_credits_devs'],
516
+					'desc' => $txt['shd_credits_devs_desc'],
517
+					'members' => array(
518
+						'Peter &quot;Arantor&quot; Spicer',
519
+						'Jason &quot;JBlaze&quot; Clemons',
520
+						'Marcus &quot;Nas&quot; Forsberg',
521
+					),
522
+				),
523
+				array(
524
+					'title' => $txt['shd_credits_projectsupport'],
525
+					'desc' => $txt['shd_credits_projectsupport_desc'],
526
+					'members' => array(
527
+						'Graeme &quot;Trekkie101&quot; Spence',
528
+						'Jeremy &quot;SleePy&quot; Darwood',
529
+					),
530
+				),
531
+				array(
532
+					'title' => $txt['shd_credits_marketing'],
533
+					'desc' => $txt['shd_credits_marketing_desc'],
534
+					'members' => array(
535
+						'Brannon &quot;B&ordf;&quot; Hall',
536
+					),
537
+				),
538
+				array(
539
+					'title' => $txt['shd_credits_globalizer'],
540
+					'desc' => $txt['shd_credits_globalizer_desc'],
541
+					'members' => array(
542
+						'Jerry Osborne',
543
+					),
544
+				),
545
+				array(
546
+					'title' => $txt['shd_credits_support'],
547
+					'desc' => $txt['shd_credits_support_desc'],
548
+					'members' => array(
549
+						'Tyrsson',
550
+						'Ha2',
551
+					),
552
+				),
553
+				array(
554
+					'title' => $txt['shd_credits_qualityassurance'],
555
+					'desc' => $txt['shd_credits_qualityassurance_desc'],
556
+					'members' => array(
557
+						'Sinan &quot;[SiNaN]&quot; &Ccedil;evik',
558
+						'Alex &quot;Cleo&quot; Tokar',
559
+					),
560
+				),
561
+				array(
562
+					'title' => $txt['shd_credits_beta'],
563
+					'desc' => $txt['shd_credits_beta_desc'],
564
+					'members' => array(
565
+						'Chris &quot;ccbtimewiz&quot; Batista',
566
+						'Wade &quot;Acans&quot; Poulsen',
567
+						'Aldo &quot;hadesflames&quot; Barreras',
568
+						'tfs',
569
+						'Edwin &quot;Dismal Shadow&quot; Mendez',
570
+						'Treznax',
571
+						'Mark &quot;KiLLuMiNaTi&minus;7&minus;&quot; Longworth',
572
+						'NIBOGO',
573
+						'Robert &quot;Robbo&quot; Clancy',
574
+						'Ya&#x11F;izcan Arslan',
575
+					),
576
+				),
577
+				array(
578
+					'title' => $txt['shd_credits_alltherest'],
579
+					'desc' => '', // This group has its description included in the title.
580
+					'members' => array(
581
+						'Fluffy - ' . sprintf($txt['shd_fluffy'],'onclick="window.location.href=\'' . $scripturl . '?action=admin;area=helpdesk_info;cookies\'"'),
582
+						'<br /><a href="http://led24.de/iconset/">LED Icon Set</a> - ' . $txt['shd_credits_ledicons'],
583
+						'<br />' . $txt['shd_credits_user'],
584
+					),
585
+				),
586
+			),
587
+		),
588
+	);
589
+}
590
+
591
+/**
592
+ *	@ignore
593
+ *	@since 1.0
594
+*/
595
+function shd_do_important()
596
+{
597
+	// Execute code number 66.
598
+	die(base64_decode('PCFET0NUWVBFIGh0bWwgUFVCTElDICItLy9XM0MvL0RURCBYSFRNTCAxLjAgVHJhbnNpdGlvbmFsLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL1RSL3hodG1sMS9EVEQveGh0bWwxLXRyYW5zaXRpb25hbC5kdGQiPjxodG1sIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIj48aGVhZD48dGl0bGU+Rmx1ZmZ5IC0gR3VhcmRpYW4gb2YgdGhlIGNvb2tpZXo8L3RpdGxlPjxtZXRhIGh0dHAtZXF1aXY9IkNvbnRlbnQtVHlwZSIgY29udGVudD0idGV4dC9odG1sOyBjaGFyc2V0PVVURi04IiAvPjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Ym9keXtiYWNrZ3JvdW5kOnB1cnBsZTtjb2xvcjp3aGl0ZTtmb250LXNpemU6MzBweDt0ZXh0LWFsaWduOmNlbnRlcjt9PC9zdHlsZT48L2hlYWQ+PGJvZHk+PGltZyBzcmM9Imh0dHA6Ly93d3cuc2ltcGxlZGVzay5uZXQvaW1hZ2VzL3NpdGUvZ3VhcmRkb2c0MDMuanBnIiBhbHQ9IiIgb25jbGljaz0iYWxlcnQoJ1dhcm5pbmc6IEhlIG1heSBiaXRlIScpIi8+PGJyIC8+PGJyIC8+PHN0cm9uZz5GbHVmZnk8L3N0cm9uZz4gc2VlcyB5b3UuIEZsdWZmeSB3aWxsIHByb3RlY3Qgb3VyIGNhbmR5IDxzdHJvbmc+Zm9yIGV2ZXI8L3N0cm9uZz4uIEZsdWZmeSBpcyB0aGUgPHN0cm9uZz5ndWFyZGlhbjwvc3Ryb25nPiBvZiB0aGUgPHN0cm9uZz5jb29raWVzPC9zdHJvbmc+LiBCZSBhd2FyZS48L2JvZHk+PC9odG1sPg==')); // It will be done, my lord.
599
+}
600
+
601
+?>
0 602
\ No newline at end of file
... ...
@@ -0,0 +1,361 @@
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: 1.0 Felidae                             #
17
+# File Info: SimpleDesk-AjaxHandler.php / 1.0 Felidae         #
18
+###############################################################
19
+
20
+/**
21
+ *	This file handles all the actions that can be carried out through AJAX methods, performing them and providing adequate feedback/information.
22
+ *
23
+ *	@package source
24
+ *	@since 1.0
25
+*/
26
+
27
+if (!defined('SMF'))
28
+	die('Hacking attempt...');
29
+
30
+// This file handles AJAX actions. This file accepts params and only dumps XML out, much like News.php we do it all here.
31
+// What happens is we expect $context['ajax_return'], an array of responses, to be populated.
32
+// key => value for single XML tag, key => array (values) for multiple tags
33
+// e.g. array('errors' => 'You do not have permission') => <errors><![CDATA[You do not have permission]]></errors>
34
+// or array('data' => array('myval1', 'myval2')) => <data><![CDATA[myval1]]></data> <data><![CDATA[myval2]]></data>
35
+
36
+/**
37
+ *	Receives AJAX requests and facilitates replying to them.
38
+ *
39
+ *	This function sets up and calls the AJAX handlers; it is the primary receiver for action=helpdesk;sa=ajax.
40
+ *
41
+ *	Primarily, it expects $context['ajax_return'] to be populated as an array of tags to be returned as XML items to the user, with this element
42
+ *	being a key-value pair (e.g. $context['ajax_return']['message'] = 'Success!') to be returned as a literal tag, element message, value of
43
+ *	Success!, wrapped in the character data block.
44
+ *
45
+ *	An AJAX handler function may also return content in $context['ajax_raw'], this is when the function has prepared its own XML block to return.
46
+ *	In such an instance, this function simply outputs the xml headers and assumes the return value is otherwise value.
47
+ *
48
+ *	For example, if the error subarray is populated (['error'] = 'Insufficient permission'), the XML block will contain a tag called error,
49
+ *	with a CDATA block containing 'Insufficient permission'.
50
+ *
51
+ *	@since 1.0
52
+*/
53
+function shd_ajax()
54
+{
55
+	global $context;
56
+
57
+	// Just in case
58
+	loadLanguage('Errors');
59
+
60
+	$subactions = array(
61
+		'privacy' => 'shd_ajax_privacy',
62
+		'urgency' => 'shd_ajax_urgency',
63
+		'quote' => 'shd_ajax_quote',
64
+	);
65
+
66
+	$context['ajax_return'] = array();
67
+	$context['ajax_raw'] = '';
68
+
69
+	if (!empty($_REQUEST['op']) && !empty($subactions[$_REQUEST['op']]))
70
+		$subactions[$_REQUEST['op']]();
71
+
72
+	header('Content-Type: text/xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set']));
73
+	echo '<?xml version="1.0" encoding="', $context['character_set'], '"?' . '>';
74
+
75
+	if (empty($context['ajax_raw'])) // if something wants to do something funky, let it otherwise use the standard format
76
+	{
77
+		echo '<response>';
78
+
79
+		if (!empty($context['ajax_return']))
80
+		{
81
+			foreach ($context['ajax_return'] as $key => $value)
82
+			{
83
+				if (empty($value)) // for <tag />
84
+					echo '
85
+	<', $key, ' />';
86
+				else
87
+				{
88
+					$value = (array) $value;
89
+					foreach ($value as $thisvalue)
90
+						echo '
91
+	<', $key, '><![CDATA[', $thisvalue, ']]></', $key, '>';
92
+				}
93
+			}
94
+		}
95
+
96
+		echo '
97
+</response>';
98
+	}
99
+	else
100
+	{
101
+		echo $context['ajax_raw']; // assumed to be just well formed XML sans the header
102
+	}
103
+	obExit(false);
104
+}
105
+
106
+/**
107
+ *	Handles AJAX updates to privacy.
108
+ *
109
+ *	Receives a request via ?action=helpdesk;sa=ajax;op=privacy to flip the privacy setting.
110
+ *
111
+ *	Operations:
112
+ *	- silent session check; if fail, add to $context['ajax_return']['error']
113
+ *	- ticket id check; if fail, add to $context['ajax_return']['error']
114
+ *	- get enough ticket data to do this
115
+ *	- check if the ticket is not able to be updated (either is closed/deleted, or insufficient permissions); if fail add to $context['ajax_return']['error']
116
+ *	- switch privacy, update database
117
+ *	- clear the cache of tickets for the Helpdesk menu item
118
+ *	- return $context['ajax_return']['message'] as the new privacy item
119
+*/
120
+function shd_ajax_privacy()
121
+{
122
+	global $smcFunc, $user_info, $context, $txt, $sourcedir;
123
+
124
+	$session_check = checkSession('get', '', false); // check the session but don't die fatally.
125
+	if (!empty($session_check))
126
+	{
127
+		$context['ajax_return'] = array('error' => $txt[$session_check]);
128
+		return;
129
+	}
130
+
131
+	// First, figure out the state of the ticket - is it private or not? Can we even see it?
132
+	if (empty($context['ticket_id']))
133
+	{
134
+		$context['ajax_return'] = array('error' => $txt['shd_no_ticket']);
135
+		return;
136
+	}
137
+
138
+	$query = shd_db_query('', '
139
+		SELECT id_member_started, subject, private, status
140
+		FROM {db_prefix}helpdesk_tickets AS hdt
141
+		WHERE {query_see_ticket}
142
+			AND id_ticket = {int:current_ticket}',
143
+		array(
144
+			'current_ticket' => $context['ticket_id'],
145
+		)
146
+	);
147
+
148
+	if ($row = $smcFunc['db_fetch_assoc']($query))
149
+	{
150
+		if (in_array($row['status'], array(TICKET_STATUS_CLOSED, TICKET_STATUS_DELETED)) || !shd_allowed_to('shd_alter_privacy_any') && (!shd_allowed_to('shd_alter_privacy_own') || $row['id_member_started'] != $user_info['id']))
151
+		{
152
+			$context['ajax_return'] = array('error' => $txt['shd_cannot_change_privacy']);
153
+			return;
154
+		}
155
+
156
+		$smcFunc['db_free_result']($query);
157
+
158
+		$new = empty($row['private']) ? 1 : 0;
159
+		$action = empty($row['private']) ? 'markprivate' : 'marknotprivate';
160
+
161
+		require_once($sourcedir . '/Subs-SimpleDeskPost.php');
162
+		$msgOptions = array();
163
+		$posterOptions = array();
164
+		$ticketOptions = array(
165
+			'id' => $context['ticket_id'],
166
+			'private' => empty($row['private']),
167
+		);
168
+
169
+		shd_modify_ticket_post($msgOptions, $ticketOptions, $posterOptions);
170
+
171
+		shd_log_action($action,
172
+			array(
173
+				'ticket' => $context['ticket_id'],
174
+				'subject' => $row['subject'],
175
+			)
176
+		);
177
+
178
+		// Make sure we recalculate the number of tickets on next page load
179
+		shd_clear_active_tickets($row['id_member_started']);
180
+
181
+		$context['ajax_return'] = array('message' => $new ? $txt['shd_ticket_private'] : $txt['shd_ticket_notprivate']);
182
+	}
183
+	else
184
+	{
185
+		$context['ajax_return'] = array('error' => $txt['shd_no_ticket']);
186
+		return;
187
+	}
188
+}
189
+
190
+/**
191
+ *	Handles AJAX updates to ticket urgency.
192
+ *
193
+ *	Receives request through ?action=helpdesk;sa=ajax;op=urgency;change=x where x is increase or decrease.
194
+ *
195
+ *	Operations:
196
+ *	- silent session check; if fail, add to $context['ajax_return']['error']
197
+ *	- ticket id check; if fail, add to $context['ajax_return']['error']
198
+ *	- get enough ticket data to do this
199
+ *	- check permissions with {@link shd_can_alter_urgency()} and permissions; if fail, add to $context['ajax_return']['error']
200
+ *	- identify whether the new urgency needs 'urgent' styling or not and put the new urgency in $context['ajax_return']['message']
201
+ *	- update the database with the new urgency
202
+ *	- identify whether the new urgency continues to allow the current user to change urgency or not
203
+ *	- put the button links if appropriate into $context['ajax_return']['increase'] and $context['ajax_return']['decrease'] and return
204
+*/
205
+function shd_ajax_urgency()
206
+{
207
+	global $smcFunc, $user_info, $context, $txt, $scripturl, $settings, $sourcedir;
208
+
209
+	$session_check = checkSession('get', '', false); // check the session but don't die fatally.
210
+	if (!empty($session_check))
211
+	{
212
+		$context['ajax_return'] = array('error' => $txt[$session_check]);
213
+		return;
214
+	}
215
+
216
+	// First, figure out the state of the ticket - is it private or not? Can we even see it?
217
+	if (empty($context['ticket_id']))
218
+	{
219
+		$context['ajax_return'] = array('error' => $txt['shd_no_ticket']);
220
+		return;
221
+	}
222
+
223
+	$query = shd_db_query('', '
224
+		SELECT id_member_started, subject, urgency, status
225
+		FROM {db_prefix}helpdesk_tickets AS hdt
226
+		WHERE {query_see_ticket}
227
+			AND id_ticket = {int:current_ticket}',
228
+		array(
229
+			'current_ticket' => $context['ticket_id'],
230
+		)
231
+	);
232
+
233
+	if ($row = $smcFunc['db_fetch_assoc']($query))
234
+	{
235
+		$can_urgency = shd_can_alter_urgency($row['urgency'], $row['id_member_started'], ($row['status'] == TICKET_STATUS_CLOSED), ($row['status'] == TICKET_STATUS_DELETED));
236
+
237
+		if (empty($_GET['change']) || empty($can_urgency[$_GET['change']]))
238
+		{
239
+			$context['ajax_return'] = array('error' => $txt['shd_cannot_change_urgency']);
240
+			return;
241
+		}
242
+
243
+		$new_urgency = $row['urgency'] + ($_GET['change'] == 'increase' ? 1 : -1);
244
+		$action = 'urgency_' . $_GET['change'];
245
+
246
+		require_once($sourcedir . '/Subs-SimpleDeskPost.php');
247
+		$msgOptions = array();
248
+		$posterOptions = array();
249
+		$ticketOptions = array(
250
+			'id' => $context['ticket_id'],
251
+			'urgency' => $new_urgency,
252
+		);
253
+
254
+		shd_modify_ticket_post($msgOptions, $ticketOptions, $posterOptions);
255
+
256
+		shd_log_action($action,
257
+			array(
258
+				'ticket' => $context['ticket_id'],
259
+				'subject' => $row['subject'],
260
+				'urgency' => $new_urgency,
261
+			)
262
+		);
263
+
264
+		$new_options = shd_can_alter_urgency($new_urgency, $row['id_member_started'], ($row['status'] == TICKET_STATUS_CLOSED), ($row['status'] == TICKET_STATUS_DELETED));
265
+
266
+		$context['ajax_return'] = array(
267
+			'message' => $new_urgency > TICKET_URGENCY_HIGH ? '<span class="error">' . $txt['shd_urgency_' . $new_urgency] . '</span>' : $txt['shd_urgency_' . $new_urgency],
268
+		);
269
+
270
+		$array = array(
271
+			'increase' => '<a id="urglink_increase" href="' . $scripturl . '?action=helpdesk;sa=urgencychange;ticket=' . $context['ticket_id'] . ';change=increase;' . $context['session_var'] . '=' . $context['session_id'] . '" title="' . $txt['shd_urgency_increase'] . '"><img src="' . $settings['images_url'] . '/sort_up.gif" width="9px" alt="' . $txt['shd_urgency_increase'] . '" /></a>',
272
+			'decrease' => '<a id="urglink_decrease" href="' . $scripturl . '?action=helpdesk;sa=urgencychange;ticket=' . $context['ticket_id'] . ';change=decrease;' . $context['session_var'] . '=' . $context['session_id'] . '" title="' . $txt['shd_urgency_decrease'] . '"><img src="' . $settings['images_url'] . '/sort_down.gif" width="9px" alt="' . $txt['shd_urgency_decrease'] . '" /></a>',
273
+		);
274
+
275
+		foreach ($new_options as $button => $can)
276
+			if ($can)
277
+				$context['ajax_return'][$button] = $array[$button];
278
+
279
+		return;
280
+	}
281
+	else
282
+	{
283
+		$context['ajax_return'] = array('error' => $txt['shd_no_ticket']);
284
+		return;
285
+	}
286
+}
287
+
288
+/**
289
+ *	Collects ticket post data for quoting posts through AJAX (i.e. inserting a quote live into the postbox)
290
+ *
291
+ *	Operations:
292
+ *	- Session check; failing in a regular fashion (as opposed to normal return since we're using ;xml in the URL; the SMF handler can deal with that)
293
+ *	- If a message id is provided, query for it. If not found (or not provided), die, otherwise continue.
294
+ *	- Call un_preparsecode to remove extraneous sanity encoding.
295
+ *	- Build the [quote] bbcode around the post body.
296
+ *	- Convert to SMF style BBC-to-HTML if using WYSIWYG
297
+ *	- Do other XML sanitising
298
+ *	- Return via $context['ajax_raw'] for {@link shd_ajax()} to output
299
+*/
300
+function shd_ajax_quote()
301
+{
302
+	global $modSettings, $user_info, $txt, $settings, $context;
303
+	global $sourcedir, $smcFunc;
304
+
305
+	loadLanguage('Post');
306
+	checkSession('get');
307
+
308
+	include_once($sourcedir . '/Subs-Post.php');
309
+
310
+	$_REQUEST['quote'] = !empty($_REQUEST['quote']) ? (int) $_REQUEST['quote'] : 0;
311
+	$message = '';
312
+	if (!empty($_REQUEST['quote']))
313
+	{
314
+		$query = shd_db_query('', '
315
+			SELECT hdtr.body, IFNULL(mem.real_name, hdtr.poster_name) AS poster_name, hdtr.poster_time, hdt.id_ticket, hdt.id_first_msg
316
+			FROM {db_prefix}helpdesk_ticket_replies AS hdtr
317
+				INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hdtr.id_ticket = hdt.id_ticket)
318
+				LEFT JOIN {db_prefix}members AS mem ON (hdtr.id_member = mem.id_member)
319
+			WHERE {query_see_ticket}
320
+				AND id_msg = {int:msg}',
321
+			array(
322
+				'msg' => $_REQUEST['quote'],
323
+			)
324
+		);
325
+
326
+		if ($row = $smcFunc['db_fetch_assoc']($query))
327
+		{
328
+			$smcFunc['db_free_result']($query);
329
+			$row['body'] = un_preparsecode($row['body']);
330
+
331
+			// Censor the message!
332
+			censorText($row['body']);
333
+			$row['body'] = preg_replace('~<br ?/?' . '>~i', "\n", $row['body']);
334
+
335
+			if (strpos($row['poster_name'], '[') !== false || strpos($row['poster_name'], ']') !== false)
336
+				$row['poster_name'] = '"' . $row['poster_name'] . '"';
337
+
338
+			// Make the body HTML if need be.
339
+			if (!empty($_REQUEST['mode']))
340
+			{
341
+				require_once($sourcedir . '/Subs-Editor.php');
342
+				$row['body'] = strtr($row['body'], array('&lt;' => '#smlt#', '&gt;' => '#smgt#', '&amp;' => '#smamp#'));
343
+				$row['body'] = bbc_to_html($row['body']);
344
+				$lb = '<br />';
345
+			}
346
+			else
347
+				$lb = "\n";
348
+
349
+			$message = '[quote author=' . $row['poster_name'] . ' link=action=helpdesk;sa=ticket;ticket=' . $row['id_ticket'];
350
+			if ($row['id_first_msg'] != $_REQUEST['quote']) // don't add the msg if we're quoting the ticket itself
351
+				$message .= '.msg' . $_REQUEST['quote'] . '#msg' . $_REQUEST['quote'];
352
+
353
+			$message .= ' date=' . $row['poster_time'] . ']' . $lb . $row['body'] . $lb . '[/quote]';
354
+		}
355
+	}
356
+
357
+	$message = strtr($message, array('&nbsp;' => '&#160;', '<' => '&lt;', '>' => '&gt;'));
358
+
359
+	$context['ajax_raw'] = '<quote>' . $message . '</quote>';
360
+}
361
+?>
0 362
\ No newline at end of file
... ...
@@ -0,0 +1,359 @@
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: 1.0 Felidae                             #
17
+# File Info: SimpleDesk-Assign.php / 1.0 Felidae              #
18
+###############################################################
19
+
20
+/**
21
+ *	This file handles ticket assignments, both the user interface for the assignment page, plus
22
+ *	actually assigning users to tickets. There are also helper functions here for that purpose.
23
+ *
24
+ *	@package source
25
+ *	@since 1.0
26
+*/
27
+
28
+if (!defined('SMF'))
29
+	die('Hacking attempt...');
30
+
31
+/**
32
+ *	Sets up the assignment UI and invokes the template.
33
+ *
34
+ *	This validates that the user can see the ticket in question initially, before determining the level of assignment that can be
35
+ *	carried out.
36
+ *
37
+ *	Users who can assign any ticket cause a query to trigger to identify users who have shd_staff permission (and thus can be the
38
+ *	target of assignment) and the template is loaded with a dropdown to provide this choice to the user.
39
+ *
40
+ *	Users who can only assign to themselves will at this point have the ticket assigned to them if it was unassigned before, or
41
+ *	unassigned from them if they are able to assign tickets to themselves in the first place; if it was assigned to someone else,
42
+ *	they will not have the option.
43
+ *
44
+ *	Access through action=helpdesk;sa=assign;ticket=x;sessvar=sessid before redirecting back to the ticket, or add ;home to the
45
+ *	URL to have it redirect back to the home page (passed through to the form for {@link shd_assign2()}'s use too)
46
+ *
47
+ *	@see shd_assign2()
48
+ *	@see shd_get_possible_assignees()
49
+ *	@since 1.0
50
+*/
51
+function shd_assign()
52
+{
53
+	global $smcFunc, $context, $user_info, $sourcedir, $txt, $scripturl;
54
+
55
+	checkSession('get');
56
+
57
+	if (empty($context['ticket_id']))
58
+		fatal_lang_error('shd_no_ticket');
59
+
60
+	$context['shd_return_to'] = isset($_REQUEST['home']) ? 'home' : 'ticket';
61
+
62
+	// Get ticket details - and kick it out if they shouldn't be able to see it.
63
+	$query = shd_db_query('', '
64
+		SELECT id_member_started, id_member_assigned, private, subject
65
+		FROM {db_prefix}helpdesk_tickets AS hdt
66
+		WHERE {query_see_ticket} AND id_ticket = {int:ticket}',
67
+		array(
68
+			'ticket' => $context['ticket_id'],
69
+		)
70
+	);
71
+
72
+	$log_params = array();
73
+	if ($row = $smcFunc['db_fetch_row']($query))
74
+	{
75
+		list($ticket_starter, $ticket_owner, $private, $subject) = $row;
76
+		$log_params = array(
77
+			'subject' => $subject,
78
+			'ticket' => $context['ticket_id'],
79
+		);
80
+	}
81
+	else
82
+	{
83
+		$smcFunc['db_free_result']($query);
84
+		fatal_lang_error('shd_no_ticket');
85
+	}
86
+
87
+	if (shd_allowed_to('shd_assign_ticket_any')) // can regularly assign? If so, load up potential candidates and throw it at the template.
88
+	{
89
+		$members = shd_get_possible_assignees($private, $ticket_starter);
90
+
91
+		if (!in_array($ticket_owner, $members)) // if for whatever reason the current assignee is not accessible staff, treat it as unassigned
92
+			$ticket_owner = 0;
93
+
94
+		if (!empty($members))
95
+		{
96
+			$query = shd_db_query('', '
97
+				SELECT id_member, real_name
98
+				FROM {db_prefix}members
99
+				WHERE id_member IN ({array_int:members})
100
+				ORDER BY real_name',
101
+				array(
102
+					'members' => $members,
103
+				)
104
+			);
105
+
106
+			$members = array();
107
+
108
+			while ($row = $smcFunc['db_fetch_assoc']($query))
109
+				$members[$row['id_member']] = $row['real_name'];
110
+		}
111
+
112
+		if (empty($members))
113
+			fatal_lang_error('shd_no_staff_assign');
114
+
115
+		$context['linktree'][] = array(
116
+			'url' => $scripturl . '?action=helpdesk;sa=main',
117
+			'name' => $txt['shd_linktree_tickets'],
118
+		);
119
+		$context['linktree'][] = array(
120
+			'url' => $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $context['ticket_id'],
121
+			'name' => $subject,
122
+		);
123
+		$context['linktree'][] = array(
124
+			'name' => $txt['shd_ticket_assign_ticket'],
125
+		);
126
+
127
+		$context['member_list'] = array(0 => $txt['shd_unassigned']) + $members;
128
+		$context['ticket_assigned'] = $ticket_owner;
129
+
130
+		$context['page_title'] = $txt['shd_ticket_assign_ticket'];
131
+
132
+		loadTemplate('SimpleDesk-Assign');
133
+		$context['sub_template'] = 'assign';
134
+	}
135
+	elseif (shd_allowed_to('shd_assign_ticket_own') && shd_allowed_to('shd_staff')) // can't just randomly assign (and must be staff), so look at if it's already assigned or not.
136
+	{
137
+		if ($ticket_owner == 0) // unassigned
138
+		{
139
+			$log_params += array(
140
+				'user_id' => $user_info['id'],
141
+				'user_name' => $user_info['name'],
142
+			);
143
+			shd_log_action('assign', $log_params);
144
+			shd_commit_assignment($context['ticket_id'], $user_info['id']);
145
+		}
146
+		elseif ($ticket_starter == $user_info['id']) // assigned to self already, unassign it
147
+		{
148
+			shd_log_action('unassign', $log_params);
149
+			shd_commit_assignment($context['ticket_id'], 0);
150
+		}
151
+		else // oops, assigned to somebody else
152
+			fatal_lang_error('shd_cannot_assign_other', false);
153
+	}
154
+	else
155
+		fatal_lang_error('shd_cannot_assign', false);
156
+}
157
+
158
+/**
159
+ *	Handles the actual assignment form, validates it and carries it out.
160
+ *
161
+ *	Primarily this is just about receiving the form, making the same checks that {@link shd_assign()} does and then
162
+ *	logging the action before passing over to {@link shd_commit_assignment()} to actually assign the ticket.
163
+ *
164
+ *	@see shd_assign()
165
+ *	@see shd_commit_assignment()
166
+ *	@since 1.0
167
+*/
168
+function shd_assign2()
169
+{
170
+	global $context, $smcFunc, $user_info, $sourcedir;
171
+
172
+	checkSession();
173
+	checkSubmitOnce('check');
174
+
175
+	if (empty($context['ticket_id']))
176
+		fatal_lang_error('shd_no_ticket');
177
+
178
+	$context['shd_return_to'] = isset($_REQUEST['home']) ? 'home' : 'ticket';
179
+
180
+	$assignee = isset($_REQUEST['to_user']) ? (int) $_REQUEST['to_user'] : 0;
181
+
182
+	// Get ticket details - and kick it out if they shouldn't be able to see it.
183
+	$query = shd_db_query('', '
184
+		SELECT id_member_started, id_member_assigned, private, subject
185
+		FROM {db_prefix}helpdesk_tickets AS hdt
186
+		WHERE {query_see_ticket} AND id_ticket = {int:ticket}',
187
+		array(
188
+			'ticket' => $context['ticket_id'],
189
+		)
190
+	);
191
+
192
+	$log_params = array();
193
+
194
+	if ($row = $smcFunc['db_fetch_row']($query))
195
+	{
196
+		list($ticket_starter, $ticket_owner, $private, $subject) = $row;
197
+
198
+		// The core details that we'll be logging
199
+		$log_params = array(
200
+			'subject' => $subject,
201
+			'ticket' => $context['ticket_id'],
202
+		);
203
+	}
204
+	else
205
+	{
206
+		$smcFunc['db_free_result']($query);
207
+		fatal_lang_error('shd_no_ticket');
208
+	}
209
+
210
+	// Just in case, are they cancelling?
211
+	if (isset($_REQUEST['cancel']))
212
+		redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id']);
213
+
214
+	if (shd_allowed_to('shd_assign_ticket_any')) // can regularly assign? If so, see if our requested member is staff and can see the ticket
215
+	{
216
+		if ($assignee == 0) // can always unassign a ticket
217
+		{
218
+			shd_log_action('unassign', $log_params);
219
+			shd_commit_assignment($context['ticket_id'], 0);
220
+		}
221
+		else
222
+		{
223
+			$members = shd_get_possible_assignees($private, $ticket_starter);
224
+
225
+			if (in_array($assignee, $members)) // can only assign a ticket to a member if they're on Santa's good list
226
+			{
227
+				global $user_profile;
228
+				loadMemberData($assignee, false, 'minimal');
229
+				$log_params += array(
230
+					'user_id' => $assignee,
231
+					'user_name' => $user_profile[$assignee]['real_name'],
232
+				);
233
+				shd_log_action('assign', $log_params);
234
+				shd_commit_assignment($context['ticket_id'], $assignee);
235
+			}
236
+			else
237
+				fatal_lang_error('shd_assigned_not_permitted', false);
238
+		}
239
+	}
240
+	elseif (shd_allowed_to('shd_assign_ticket_own') && shd_allowed_to('shd_staff')) // can't just randomly assign (and must be staff), so look at if it's already assigned or not.
241
+	{
242
+		if ($ticket_owner == 0) // unassigned
243
+		{
244
+			$log_params += array(
245
+				'user_id' => $user_info['id'],
246
+				'user_name' => $user_info['name'],
247
+			);
248
+			shd_log_action('assign', $log_params);
249
+			shd_commit_assignment($context['ticket_id'], $user_info['id']);
250
+		}
251
+		elseif ($ticket_starter == $user_info['id']) // assigned to self already, unassign it
252
+		{
253
+			shd_log_action('unassign', $log_params);
254
+			shd_commit_assignment($context['ticket_id'], 0);
255
+		}
256
+		else // oops, assigned to somebody else
257
+			fatal_lang_error('shd_cannot_assign_other', false);
258
+	}
259
+	else
260
+		fatal_lang_error('shd_cannot_assign', false);
261
+}
262
+
263
+/**
264
+ *	Actually commits a ticket assignment to the database.
265
+ *
266
+ *	This function sets up the relevant options before handing over to shd_modify_ticket_post() which is the preferred way to modify
267
+ *	tickets generally. No permissions check is performed here.
268
+ *
269
+ *	Relies on $context['shd_return_to'] to have been set, defaults to 'ticket' to return to ticket, otherwise return to home page
270
+ *	on value of 'home'.
271
+ *
272
+ *	@param int $ticket Ticket id number, assumed to exist.
273
+ *	@param int $assignment User id of the user to which this ticket will be assigned; can be 0 to unassign a ticket
274
+ *
275
+ *	@see shd_assign()
276
+ *	@see shd_assign2()
277
+*/
278
+function shd_commit_assignment($ticket, $assignment)
279
+{
280
+	global $smcFunc, $sourcedir, $context;
281
+
282
+	require($sourcedir . '/Subs-SimpleDeskPost.php');
283
+
284
+	$msgOptions = array();
285
+	$posterOptions = array();
286
+	$ticketOptions = array(
287
+		'id' => $context['ticket_id'],
288
+		'assigned' => $assignment,
289
+	);
290
+
291
+	shd_modify_ticket_post($msgOptions, $ticketOptions, $posterOptions);
292
+
293
+	if (!empty($context['shd_return_to']) && $context['shd_return_to'] == 'home')
294
+		redirectexit('action=helpdesk;sa=main');
295
+	else
296
+		redirectexit('action=helpdesk;sa=ticket;ticket=' . $ticket);
297
+}
298
+
299
+/**
300
+ *	Return a list of possible users that can be assigned a ticket.
301
+ *
302
+ *	This function centralises who a ticket can be assigned to. Currently this is:
303
+ *	- user with shd_staff permission
304
+ *	- can see "any" ticket (not just their own)
305
+ *	- additionally, if the ticket is private, the user must also be able to see 'any' private ticket.
306
+ *	- additionally, check whether user is staff + ticket starter, and then whether we should block them from being assigned
307
+ *	- (since 1.1) additionally if we have set the option, which users are true admins, and if so, remove them from the list too
308
+ *
309
+ *	@param bool $private Whether the ticket in question is private or not.
310
+ *	@param int $ticket_owner User id of the ticket owner
311
+ *
312
+ *	@return array An indexed array of member ids that this ticket could be assigned to.
313
+ *	@see shd_assign()
314
+ *	@see shd_assign2()
315
+ *	@since 1.0
316
+*/
317
+function shd_get_possible_assignees($private = false, $ticket_owner = 0)
318
+{
319
+	global $context, $smcFunc, $modSettings;
320
+
321
+	// people who can handle a ticket
322
+	$staff = shd_members_allowed_to('shd_staff');
323
+
324
+	// is it private, if so, remove that list
325
+	if ((bool) $private == true)
326
+	{
327
+		$private = shd_members_allowed_to('shd_view_ticket_private_any');
328
+		$staff = array_intersect($staff, $private);
329
+	}
330
+
331
+	// What about admins? We ignoring them? Note we're talking about *real* admins who implicitly have every permission, not any homebrew
332
+	if (!empty($modSettings['shd_admins_not_assignable']))
333
+	{
334
+		$query = $smcFunc['db_query']('', '
335
+			SELECT id_member
336
+			FROM {db_prefix}members
337
+			WHERE id_group = 1
338
+				OR FIND_IN_SET(1, additional_groups)',
339
+			array()
340
+		);
341
+
342
+		$admins = array();
343
+		while ($row = $smcFunc['db_fetch_row']($query))
344
+			$admins[] = $row[0];
345
+
346
+		$smcFunc['db_free_result']($query);
347
+		$staff = array_diff($staff, $admins);
348
+	}
349
+
350
+	// can they actually see said ticket
351
+	$visible = shd_members_allowed_to('shd_view_ticket_any');
352
+
353
+	if (empty($modSettings['shd_staff_ticket_self'])) // by default, staff members can't be assigned a ticket if they started it
354
+		$staff = array_diff($staff, array($ticket_owner));
355
+
356
+	// spit back the list of staff members who can see any ticket (+private if dealt with)
357
+	return array_intersect($staff, $visible);
358
+}
359
+?>
0 360
\ No newline at end of file
... ...
@@ -0,0 +1,628 @@
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: 1.0 Felidae                             #
17
+# File Info: SimpleDesk-Delete.php / 1.0 Felidae              #
18
+###############################################################
19
+
20
+/**
21
+ *	This file deals with deletion/recycling of tickets and replies, subsequent restoration back to
22
+ *	the helpdesk, and lastly, permanent deletion of the same.
23
+ *
24
+ *	@package source
25
+ *	@todo Finish documenting this file.
26
+ *	@since 1.0
27
+*/
28
+
29
+if (!defined('SMF'))
30
+	die('Hacking attempt...');
31
+
32
+/**
33
+ *	Delete the given ticket, i.e move it to the recycling bin.
34
+ *
35
+ *	Accessed through ?action=helpdesk;sa=deleteticket;ticket=x;sessvar=sessid 
36
+ *
37
+ *	Operations:
38
+ *	- Session check
39
+ *	- Check there's a ticket id
40
+ *	- Check the ticket exists and is visible to the current user
41
+ *	- Check permission to delete ticket matches (i.e. can delete any, or can delete own and this is one of ours)
42
+ *	- Update the ticket's status to deleted
43
+ *	- Log the deletion
44
+ *	- Clear the menu's Helpdesk active items cache
45
+ *	- return to the front page
46
+ *
47
+ *	@since 1.0
48
+*/
49
+function shd_ticket_delete()
50
+{
51
+	global $smcFunc, $user_info, $context;
52
+
53
+	checkSession('get');
54
+
55
+	if (empty($context['ticket_id']))
56
+		fatal_lang_error('shd_no_ticket', false);
57
+
58
+	// Check we can actually see the ticket we're deleting, and if we can only delete our own, we are the owner
59
+	$query_ticket = shd_db_query('', '
60
+		SELECT id_ticket, id_member_started, subject
61
+		FROM {db_prefix}helpdesk_tickets AS hdt
62
+		WHERE {query_see_ticket}
63
+			AND id_ticket = {int:ticket}',
64
+		array(
65
+			'ticket' => $context['ticket_id'],
66
+		)
67
+	);
68
+
69
+	if ($row = $smcFunc['db_fetch_assoc']($query_ticket))
70
+	{
71
+		$smcFunc['db_free_result']($query_ticket);
72
+		if (!shd_allowed_to('shd_delete_ticket_any') && (!shd_allowed_to('shd_delete_ticket_own') || $user_info['id'] != $row['id_member_started']))
73
+			fatal_lang_error('shd_cannot_delete_ticket', false);
74
+	}
75
+	else
76
+	{
77
+		$smcFunc['db_free_result']($query_ticket);
78
+		fatal_lang_error('shd_no_ticket', false);
79
+	}
80
+
81
+	$subject = $row['subject'];
82
+
83
+	// Move it to deleted status
84
+	$query_ticket = shd_db_query('', '
85
+		UPDATE {db_prefix}helpdesk_tickets AS hdt
86
+		SET status = {int:status_deleted},
87
+		id_member_assigned = 0
88
+		WHERE id_ticket = {int:current_ticket}
89
+			AND {query_see_ticket}',
90
+		array(
91
+			'current_ticket' => $context['ticket_id'],
92
+			'status_deleted' => TICKET_STATUS_DELETED, // just move it, don't bother calling merge (or shd_determine_status)
93
+		)
94
+	);
95
+
96
+	shd_log_action('delete',
97
+		array(
98
+			'ticket' => $context['ticket_id'],
99
+			'subject' => $subject,
100
+		)
101
+	);
102
+
103
+	// Expire the cache of count(active tickets)
104
+	shd_clear_active_tickets($row['id_member_started']);
105
+
106
+	// Go to the home
107
+	redirectexit('action=helpdesk;sa=main');
108
+}
109
+
110
+function shd_reply_delete()
111
+{
112
+	global $smcFunc, $user_info, $context, $sourcedir;
113
+
114
+	checkSession('get');
115
+
116
+	$_REQUEST['reply'] = !empty($_REQUEST['reply']) ? (int) $_REQUEST['reply'] : 0;
117
+
118
+	if (empty($_REQUEST['reply']) || empty($context['ticket_id']))
119
+		fatal_lang_error('shd_no_ticket');
120
+
121
+	// Check we can actually see the ticket we're deleting, that this reply is in this ticket and that we can delete this reply
122
+	$query_ticket = shd_db_query('', '
123
+		SELECT hdt.id_ticket, hdtr.id_member, hdt.id_member_started, subject, status
124
+		FROM {db_prefix}helpdesk_tickets AS hdt
125
+			INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hdt.id_ticket = hdtr.id_ticket)
126
+		WHERE {query_see_ticket}
127
+			AND hdt.id_ticket = {int:ticket}
128
+			AND hdtr.id_msg = {int:reply}
129
+			AND hdt.id_first_msg != {int:reply2}',
130
+		array(
131
+			'ticket' => $context['ticket_id'],
132
+			'reply' => $_REQUEST['reply'],
133
+			'reply2' => $_REQUEST['reply'], // since we can't delete the first message through the reply interface!
134
+		)
135
+	);
136
+
137
+	if ($row = $smcFunc['db_fetch_assoc']($query_ticket))
138
+	{
139
+		$smcFunc['db_free_result']($query_ticket);
140
+		if (($row['status'] == TICKET_STATUS_CLOSED || $row['status'] == TICKET_STATUS_DELETED) || (!shd_allowed_to('shd_delete_ticket_any') && (!shd_allowed_to('shd_delete_ticket_own') || $user_info['id'] != $row['id_member'])))
141
+			fatal_lang_error('shd_cannot_delete_reply', false);
142
+	}
143
+	else
144
+	{
145
+		$smcFunc['db_free_result']($query_ticket);
146
+		fatal_lang_error('shd_no_ticket', false);
147
+	}
148
+
149
+	$subject = $row['subject'];
150
+
151
+	// OK, let's clear this one, hasta la vista... ticket.
152
+	shd_db_query('', '
153
+		UPDATE {db_prefix}helpdesk_ticket_replies
154
+		SET message_status = {int:msg_status_deleted}
155
+		WHERE id_msg = {int:reply}',
156
+		array(
157
+			'msg_status_deleted' => MSG_STATUS_DELETED,
158
+			'reply' => $_REQUEST['reply'],
159
+		)
160
+	);
161
+
162
+	// Logtastic!
163
+	shd_log_action('delete_reply',
164
+		array(
165
+			'ticket' => $context['ticket_id'],
166
+			'subject' => $subject,
167
+			'msg' => $_REQUEST['reply'],
168
+		)
169
+	);
170
+
171
+	// OK, last but definitely not least, update num_replies and id_last_msg on the old ticket, and fix the old ticket's status
172
+	list($starter, $replier, $num_replies) = shd_recalc_ids($context['ticket_id']);
173
+	$query_reply = shd_db_query('', '
174
+		UPDATE {db_prefix}helpdesk_tickets
175
+		SET status = {int:status}
176
+		WHERE id_ticket = {int:ticket}',
177
+		array(
178
+			'ticket' => $context['ticket_id'],
179
+			'status' => shd_determine_status('deletereply', $starter, $replier, $num_replies),
180
+		)
181
+	);
182
+
183
+	// Expire the cache of count(active tickets)
184
+	shd_clear_active_tickets($row['id_member_started']);
185
+
186
+	// Back to the ticket
187
+	redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id']);
188
+}
189
+
190
+// Delete the given reply or ticket from the database. This is the final deletion which cannot be undone.
191
+function shd_perma_delete()
192
+{
193
+	global $smcFunc, $user_info, $context, $sourcedir;
194
+
195
+	checkSession('get');
196
+	shd_is_allowed_to('shd_delete_recycling');
197
+
198
+	// We have to have either a ticket or a reply to know what to delete (Or do you want me to drop your whole database? >:D)
199
+	if (empty($context['ticket_id']) && empty($_REQUEST['reply']))
200
+		fatal_lang_error('shd_no_ticket', false);
201
+
202
+	// If we're deleting a whole ticket...
203
+	if (!empty($context['ticket_id']) && empty($_REQUEST['reply']))
204
+	{
205
+		// Can we even see this ticket?
206
+		$query_ticket = shd_db_query('', '
207
+			SELECT id_ticket, subject, id_member_started, status
208
+			FROM {db_prefix}helpdesk_tickets AS hdt
209
+			WHERE {query_see_ticket}
210
+				AND id_ticket = {int:ticket}',
211
+			array(
212
+				'ticket' => $context['ticket_id'],
213
+			)
214
+		);
215
+
216
+		if ($smcFunc['db_num_rows']($query_ticket) == 0)
217
+		{
218
+			$smcFunc['db_free_result']($query_ticket);
219
+			fatal_lang_error('shd_no_ticket', false);
220
+		}
221
+		else
222
+		{
223
+			$row = $smcFunc['db_fetch_assoc']($query_ticket);
224
+			$smcFunc['db_free_result']($query_ticket);
225
+		}
226
+
227
+		if ($row['status'] != TICKET_STATUS_DELETED)
228
+			fatal_lang_error('shd_cannot_delete_ticket', false);
229
+
230
+		$subject = $row['subject'];
231
+		// Expire the cache of count(active tickets)
232
+		shd_clear_active_tickets($row['id_member_started']);
233
+
234
+		// Start by deleting the actual ticket.
235
+		shd_db_query('', '
236
+			DELETE FROM {db_prefix}helpdesk_tickets
237
+			WHERE id_ticket = {int:current_ticket}',
238
+			array(
239
+				'current_ticket' => $context['ticket_id'],
240
+			)
241
+		);
242
+
243
+		// Then remove any replies associated with it.
244
+		shd_db_query('', '
245
+			DELETE FROM {db_prefix}helpdesk_ticket_replies
246
+			WHERE id_ticket = {int:current_ticket}',
247
+			array(
248
+				'current_ticket' => $context['ticket_id'],
249
+			)
250
+		);
251
+
252
+		// And attachments... work out which attachments that is
253
+		$query = shd_db_query('', '
254
+			SELECT id_attach
255
+			FROM {db_prefix}helpdesk_attachments
256
+				WHERE id_ticket = {int:ticket}',
257
+			array(
258
+				'ticket' => $context['ticket_id'],
259
+			)
260
+		);
261
+
262
+		$attachIDs = array();
263
+		while ($row = $smcFunc['db_fetch_row']($query))
264
+			$attachIDs[] = $row[0];
265
+
266
+		$smcFunc['db_free_result']($query);
267
+
268
+		if (!empty($attachIDs))
269
+		{
270
+			// OK, so we have some ids
271
+			require_once($sourcedir . '/ManageAttachments.php');
272
+			$attachmentQuery = array(
273
+				'attachment_type' => 0,
274
+				'id_msg' => 0,
275
+				'id_attach' => $attachIDs,
276
+			);
277
+			removeAttachments($attachmentQuery);
278
+		}
279
+
280
+		shd_log_action('permadelete',
281
+			array(
282
+				'ticket' => $context['ticket_id'],
283
+				'subject' => $subject,
284
+			)
285
+		);
286
+
287
+		redirectexit('action=helpdesk;sa=recyclebin');
288
+	}
289
+	// Or just a single reply...
290
+	elseif (!empty($_REQUEST['reply']))
291
+	{
292
+		// Check we can actually see the ticket we're deleting, that this reply is in this ticket and that we can delete this reply
293
+		$query_ticket = shd_db_query('', '
294
+			SELECT hdt.id_ticket, hdtr.id_member, hdt.subject, hdt.id_member_started, hdt.status, hdtr.message_status
295
+			FROM {db_prefix}helpdesk_tickets AS hdt
296
+				INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hdt.id_ticket = hdtr.id_ticket)
297
+			WHERE {query_see_ticket}
298
+				AND hdt.id_ticket = {int:ticket}
299
+				AND hdtr.id_msg = {int:reply}
300
+				AND hdt.id_first_msg != {int:reply2}',
301
+			array(
302
+				'ticket' => $context['ticket_id'],
303
+				'reply' => $_REQUEST['reply'],
304
+				'reply2' => $_REQUEST['reply'], // since we can't delete the first message through the reply interface!
305
+			)
306
+		);
307
+
308
+		if ($smcFunc['db_num_rows']($query_ticket) == 0)
309
+		{
310
+			$smcFunc['db_free_result']($query_ticket);
311
+			fatal_lang_error('shd_no_ticket', false);
312
+		}
313
+		else
314
+		{
315
+			$row = $smcFunc['db_fetch_assoc']($query_ticket);
316
+			$smcFunc['db_free_result']($query_ticket);
317
+		}
318
+
319
+		if ($row['status'] == TICKET_STATUS_DELETED || $row['message_status'] != MSG_STATUS_DELETED)
320
+			fatal_lang_error('shd_cannot_delete_reply', false);
321
+
322
+		$subject = $row['subject'];
323
+		// Expire the cache of count(active tickets)
324
+		shd_clear_active_tickets($row['id_member_started']);
325
+
326
+		// Just remove the reply.
327
+		$query_reply = shd_db_query('', '
328
+			DELETE FROM {db_prefix}helpdesk_ticket_replies
329
+			WHERE id_msg = {int:current_reply}',
330
+			array(
331
+				'current_reply' => (int) $_REQUEST['reply'],
332
+			)
333
+		);
334
+
335
+		// Now to handle attachments
336
+		$query = shd_db_query('', '
337
+			SELECT id_attach
338
+			FROM {db_prefix}helpdesk_attachments
339
+				WHERE id_msg = {int:msg}',
340
+			array(
341
+				'msg' => (int) $_REQUEST['reply'],
342
+			)
343
+		);
344
+
345
+		$attachIDs = array();
346
+		while ($row = $smcFunc['db_fetch_row']($query))
347
+			$attachIDs[] = $row[0];
348
+
349
+		$smcFunc['db_free_result']($query);
350
+
351
+		if (!empty($attachIDs))
352
+		{
353
+			// OK, so we have some ids
354
+			require_once($sourcedir . '/ManageAttachments.php');
355
+			$attachmentQuery = array(
356
+				'attachment_type' => 0,
357
+				'id_msg' => 0,
358
+				'id_attach' => $attachIDs,
359
+			);
360
+			removeAttachments($attachmentQuery);
361
+		}
362
+
363
+		shd_log_action('permadelete_reply',
364
+			array(
365
+				'ticket' => $context['ticket_id'],
366
+				'subject' => $subject,
367
+			)
368
+		);
369
+
370
+		list($starter, $replier, $num_replies) = shd_recalc_ids($context['ticket_id']);
371
+		$query_reply = shd_db_query('', '
372
+			UPDATE {db_prefix}helpdesk_tickets
373
+			SET status = {int:status}
374
+			WHERE id_ticket = {int:ticket}',
375
+			array(
376
+				'ticket' => $context['ticket_id'],
377
+				'status' => shd_determine_status('deletereply', $starter, $replier, $num_replies),
378
+			)
379
+		);
380
+
381
+		redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id']);
382
+	}
383
+	else
384
+		fatal_lang_error('shd_no_ticket');
385
+}
386
+
387
+// Restore the given ticket from the recycling bin.
388
+function shd_ticket_restore()
389
+{
390
+	global $smcFunc, $user_info, $context;
391
+
392
+	checkSession('get');
393
+
394
+	if (empty($context['ticket_id']))
395
+		fatal_lang_error('shd_no_ticket', false);
396
+
397
+	// Does the ticket we're trying to restore exist and can we see it?
398
+	$query_ticket = shd_db_query('', '
399
+		SELECT id_ticket, id_member_started, id_member_updated, subject, num_replies, status
400
+		FROM {db_prefix}helpdesk_tickets AS hdt
401
+		WHERE {query_see_ticket}
402
+			AND id_ticket = {int:ticket}',
403
+		array(
404
+			'ticket' => $context['ticket_id'],
405
+		)
406
+	);
407
+
408
+	if ($row = $smcFunc['db_fetch_assoc']($query_ticket))
409
+	{
410
+		$smcFunc['db_free_result']($query_ticket);
411
+		if ($row['status'] != TICKET_STATUS_DELETED || (!shd_allowed_to('shd_restore_ticket_any') && (!shd_allowed_to('shd_restore_ticket_own') || $user_info['id'] != $row['id_member_started'])))
412
+			fatal_lang_error('shd_cannot_restore_ticket', false);
413
+
414
+		$subject = $row['subject'];
415
+		$starter = $row['id_member_started'];
416
+		$replier = $row['id_member_updated'];
417
+		$num_replies = $row['num_replies'];
418
+	}
419
+	else
420
+	{
421
+		$smcFunc['db_free_result']($query_ticket);
422
+		fatal_lang_error('shd_no_ticket', false);
423
+	}
424
+
425
+	shd_db_query('', '
426
+		UPDATE {db_prefix}helpdesk_tickets AS hdt
427
+		SET status = {int:status_new}
428
+		WHERE id_ticket = {int:current_ticket}
429
+			AND {query_see_ticket}',
430
+		array(
431
+			'current_ticket' => $context['ticket_id'],
432
+			'status_new' => shd_determine_status('restoreticket', $starter, $replier, $num_replies),
433
+		)
434
+	);
435
+
436
+	// Expire the cache of count(active tickets)
437
+	shd_clear_active_tickets($row['id_member_started']);
438
+
439
+	shd_log_action('restore',
440
+		array(
441
+			'ticket' => $context['ticket_id'],
442
+			'subject' => $subject,
443
+		)
444
+	);
445
+
446
+	redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id']);
447
+}
448
+
449
+// Restore the given reply from the recycling bin.
450
+function shd_reply_restore()
451
+{
452
+	global $smcFunc, $user_info, $context, $sourcedir;
453
+
454
+	checkSession('get');
455
+
456
+	$_REQUEST['reply'] = empty($_REQUEST['reply']) ? 0 : (int) $_REQUEST['reply'];
457
+
458
+	if (empty($_REQUEST['reply']))
459
+		fatal_lang_error('shd_no_ticket', false);
460
+
461
+	// Check we can actually see the ticket we're restoring from, and that we can restore this reply
462
+	$query_ticket = shd_db_query('', '
463
+		SELECT hdt.id_ticket, hdtr.id_member, hdt.id_member_started, hdt.id_member_updated, hdt.num_replies, hdt.subject, hdt.status, hdtr.message_status
464
+		FROM {db_prefix}helpdesk_tickets AS hdt
465
+			INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hdt.id_ticket = hdtr.id_ticket)
466
+		WHERE {query_see_ticket}
467
+			AND hdtr.id_msg = {int:reply}
468
+			AND hdt.id_first_msg != {int:reply2}',
469
+		array(
470
+			'reply' => $_REQUEST['reply'],
471
+			'reply2' => $_REQUEST['reply'],
472
+		)
473
+	);
474
+
475
+	if ($row = $smcFunc['db_fetch_assoc']($query_ticket))
476
+	{
477
+		$smcFunc['db_free_result']($query_ticket);
478
+		if (($row['status'] == TICKET_STATUS_DELETED || $row['status'] == TICKET_STATUS_CLOSED || $row['message_status'] != MSG_STATUS_DELETED) || (!shd_allowed_to('shd_restore_reply_any') && (!shd_allowed_to('shd_restore_reply_own') || $user_info['id'] != $row['id_member'])))
479
+			fatal_lang_error('shd_cannot_restore_reply', false);
480
+
481
+		$context['ticket_id'] = (int) $row['id_ticket'];
482
+		$subject = $row['subject'];
483
+		$starter = $row['id_member_started'];
484
+		$replier = $row['id_member_updated'];
485
+		$num_replies = $row['num_replies'];
486
+	}
487
+	else
488
+	{
489
+		$smcFunc['db_free_result']($query_ticket);
490
+		fatal_lang_error('shd_no_ticket', false);
491
+	}
492
+
493
+	// OK, let's clear this one, hasta la vista... ticket.
494
+	shd_db_query('', '
495
+		UPDATE {db_prefix}helpdesk_ticket_replies
496
+		SET message_status = {int:msg_status_deleted}
497
+		WHERE id_msg = {int:reply}',
498
+		array(
499
+			'msg_status_deleted' => MSG_STATUS_NORMAL,
500
+			'reply' => $_REQUEST['reply'],
501
+		)
502
+	);
503
+
504
+	// Captain's Log, stardate 18.3.10.1010
505
+	shd_log_action('restore_reply',
506
+		array(
507
+			'ticket' => $context['ticket_id'],
508
+			'subject' => $subject,
509
+			'msg' => $_REQUEST['reply'],
510
+		)
511
+	);
512
+
513
+	// Fix the topic data
514
+	list($starter, $replier, $num_replies) = shd_recalc_ids($context['ticket_id']);
515
+	$query_reply = shd_db_query('', '
516
+		UPDATE {db_prefix}helpdesk_tickets
517
+		SET status = {int:status}
518
+		WHERE id_ticket = {int:ticket}',
519
+		array(
520
+			'ticket' => $context['ticket_id'],
521
+			'status' => shd_determine_status('restorereply', $starter, $replier, $num_replies),
522
+		)
523
+	);
524
+
525
+	// Expire the cache of count(active tickets)
526
+	shd_clear_active_tickets($row['id_member_started']);
527
+
528
+	redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id']);
529
+}
530
+
531
+/**
532
+ *	Clean up tickets that have been modified by replies being altered through restore and delete.
533
+ *
534
+ *	Operations:
535
+ *	- Identify how many deleted and non deleted replies there are in the ticket.
536
+ *	- Identify the last non deleted reply in the ticket (if there are no undeleted replies, use the ticket post itself for cohesion)
537
+ *	- Update the ticket's record with the first and last posters, as well as the correct number of active and deleted replies, and whether there are any deleted replies on the ticket generally
538
+ *
539
+ *	@param int $ticket The ticket id to recalculate.
540
+ *
541
+ *	@return array An array detailing the user id of the starter, the last replier and the number of active replies in a ticket.
542
+*/
543
+function shd_recalc_ids($ticket)
544
+{
545
+	global $smcFunc;
546
+
547
+	$query = shd_db_query('', '
548
+		SELECT hdt.id_first_msg
549
+		FROM {db_prefix}helpdesk_tickets AS hdt
550
+		WHERE hdt.id_ticket = {int:ticket}',
551
+		array(
552
+			'ticket' => $ticket,
553
+		)
554
+	);
555
+	list($first_msg) = $smcFunc['db_fetch_row']($query);
556
+	$smcFunc['db_free_result']($query);
557
+
558
+	$query = shd_db_query('', '
559
+		SELECT hdtr.message_status, COUNT(hdtr.message_status) AS messages, hdt.id_first_msg, MAX(hdtr.id_msg) AS id_last_msg
560
+		FROM {db_prefix}helpdesk_ticket_replies AS hdtr
561
+			INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hdt.id_ticket = hdtr.id_ticket)
562
+		WHERE hdtr.id_msg > hdt.id_first_msg
563
+			AND hdt.id_ticket = {int:ticket}
564
+		GROUP BY hdtr.message_status',
565
+		array(
566
+			'ticket' => $ticket,
567
+		)
568
+	);
569
+
570
+	$messages = array(
571
+		MSG_STATUS_NORMAL => 0, // message_status = 0
572
+		MSG_STATUS_DELETED => 0, // message_status = 1
573
+	);
574
+
575
+	$last_msg = 0;
576
+	while ($row = $smcFunc['db_fetch_assoc']($query))
577
+	{
578
+		$first_msg = $row['id_first_msg'];
579
+		$messages[$row['message_status']] = $row['messages'];
580
+		if ($row['message_status'] == MSG_STATUS_NORMAL)
581
+			$last_msg = $row['id_last_msg'];
582
+	}
583
+
584
+	$smcFunc['db_free_result']($query);
585
+
586
+	if (empty($last_msg))
587
+		$last_msg = $first_msg;
588
+
589
+	// OK, so we have the last message id and correct number of replies, which is awesome. Now we need to ensure user ids are right
590
+	$query = shd_db_query('', '
591
+		SELECT hdtr_first.id_member, hdtr_last.id_member
592
+		FROM {db_prefix}helpdesk_tickets AS hdt
593
+			INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr_first ON (hdtr_first.id_msg = {int:first_msg})
594
+			INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr_last ON (hdtr_last.id_msg = {int:last_msg})
595
+		WHERE hdt.id_ticket = {int:ticket}',
596
+		array(
597
+			'first_msg' => $first_msg,
598
+			'last_msg' => $last_msg,
599
+			'ticket' => $ticket,
600
+		)
601
+	);
602
+	list($starter, $replier) = $smcFunc['db_fetch_row']($query);
603
+	$smcFunc['db_free_result']($query);
604
+
605
+	shd_db_query('', '
606
+		UPDATE {db_prefix}helpdesk_tickets
607
+		SET num_replies = {int:num_replies},
608
+			deleted_replies = {int:deleted_replies},
609
+			id_last_msg = {int:last_msg},
610
+			id_member_started = {int:starter},
611
+			id_member_updated = {int:replier},
612
+			withdeleted = {int:has_deleted}
613
+		WHERE id_ticket = {int:ticket}',
614
+		array(
615
+			'num_replies' => $messages[MSG_STATUS_NORMAL],
616
+			'deleted_replies' => $messages[MSG_STATUS_DELETED],
617
+			'last_msg' => $last_msg,
618
+			'starter' => $starter,
619
+			'replier' => $replier,
620
+			'has_deleted' => ($messages[MSG_STATUS_DELETED] > 0) ? 1 : 0,
621
+			'ticket' => $ticket,
622
+		)
623
+	);
624
+
625
+	return array($starter, $replier, $messages[MSG_STATUS_NORMAL]);
626
+}
627
+
628
+?>
0 629
\ No newline at end of file
... ...
@@ -0,0 +1,725 @@
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: 1.0 Felidae                             #
17
+# File Info: SimpleDesk-Display.php / 1.0 Felidae             #
18
+###############################################################
19
+
20
+/**
21
+ *	This file sets up the regular ticket view, including the menu of operations that can
22
+ *	be applied to a ticket. It is also resposible for setting up the AJAX items for modifying privacy and urgency from
23
+ *	the ticket view itself, as well as all the handling responsible for Quick Reply including the 'Go Advanced' mode.
24
+ *
25
+ *	@package source
26
+ *	@since 1.0
27
+*/
28
+
29
+if (!defined('SMF'))
30
+	die('Hacking attempt...');
31
+
32
+/**
33
+ *	Loads all the data and sets all the options for displaying a ticket.
34
+ *
35
+ *	This function does a lot of work in setting up a ticket to be displayed:
36
+ *	<ul>
37
+ *		<li>Invokes shd_load_ticket() to get the principle data</li>
38
+ *		<li>Creates $context['ticket'] to hold the data block, some of which is derived from the return of shd_load_ticket():
39
+ *			<ul>
40
+ *				<li>id: regular numeric ticket id</li>
41
+ *				<li>display_id: zero padded ticket id (e.g. 00001)</li>
42
+ *				<li>subject: censored version of the subject</li>
43
+ *				<li>first_msg: id of the opening post that forms the ticket body</li>
44
+ *				<li>body: formatted (parsed for smileys and bbcode) version of the ticket post</li>
45
+ *				<li>id_member: user id of the ticket's poster</li>
46
+ *				<li>id_member_assigned: user id of the ticket's assigned user</li>
47
+ *				<li>member: hash array of the ticket poster's details:
48
+ *					<ul>
49
+ *						<li>id: their user id</li>
50
+ *						<li>name: the name stated in the ticket post for that use</li>
51
+ *						<li>link: link to the profile of the user</li>
52
+ *					</ul>
53
+ *				</li>
54
+ *				<li>assigned: hash array of the assignee of the ticket:
55
+ *					<ul>
56
+ *						<li>id: their user id</li>
57
+ *						<li>name: name of the assignee, or 'Unassigned'</li>
58
+ *						<li>link: a full HTML link to their profile, or 'Unassigned' in red text</li>
59
+ *					</ul>
60
+ *				</li>
61
+ *				<li>assigned_self: boolean, whether the ticket is assigned to the current user or not</li>
62
+ *				<li>ticket_opener: boolean, whether the current user is the user who opened this ticket</li>
63
+ *				<li>urgency: hash array
64
+ *					<ul>
65
+ *						<li>level: numeric identifier of current ticket urgency</li>
66
+ *						<li>label: the HTML label of the urgency, including being in red for "Very High" or above</li>
67
+ *						<li>increase: Boolean, whether the current ticket urgency can be increased given the current ticket state and user permissions</li>
68
+ *						<li>decrease: Boolean, whether the current ticket urgency can be increased given the current ticket state and user permissions</li>
69
+ *					</ul>
70
+ *				</li>
71
+ *				<li>status: hash array
72
+ *					<ul>
73
+ *						<li>level: numeric, current status identifier</li>
74
+ *						<li>label: string representing the current status</li>
75
+ *					</ul>
76
+ *				<li>num_replies: the number of replies to the ticket so far</li>
77
+ *				<li>deleted_replies: how many deleted replies in this ticket</li>
78
+ *				<li>poster_time: formatted string containing the time the ticket was opened</li>
79
+ *				<li>privacy: hash array
80
+ *					<ul>
81
+ *						<li>label: current label to be used with the privacy item</li>
82
+ *						<li>can_change: Boolean, whether the user's permission with this ticket allows us to edit the ticket privacy</li>
83
+ *					</ul>
84
+ *				</li>
85
+ *				<li>closed: Boolean, represents whether this ticket is closed (used a lot with the navigation menu)</li>
86
+ *				<li>deleted: Boolean, represents whether this ticket is deleted (used a lot with the navigation menu)</li>
87
+ *				<li>ip_address: IP address logged at the time the ticket was opened; if moderate_forum_members permission is available, this will be a link to the track IP area</li>
88
+ *				<li>modified: if the ticket has been modified, also get the modified details:
89
+ *					<ul>
90
+ *						<li>id: user id who edited the ticket (not always available)</li>
91
+ *						<li>time: formatted string of the time the post was edited</li>
92
+ *						<li>timestamp: raw timestamp of the time the post was edited</li>
93
+ *						<li>name: user name of the editing user; if we have a definite user id, this should contain the current name, falling back to the previously stored name</li>
94
+ *						<li>link: if we have a known, valid user id for the post's editor, this will contain a link to their profile, with the link text using their current display name; alternatively it will contain a regular string which is the username stored with the edit.</li>
95
+ *					</ul>
96
+ *				</li>
97
+ *				<li>display_recycle: Either holds the $txt identifier of the message to apply as a warning, or false if displaying of recycling stuff in this ticket isn't appropriate (either for permissions or just because of no deleted replies, or we're just in regular ticket view)</li>
98
+ *			</ul>
99
+ *		</li>
100
+ *		<li>define the page index with SMF's constructPageIndex</li>
101
+ *		<li>query for all the ids of messages we might display, followed by querying for the message details themselves, pushing that query resource to $reply_request so we can use it in shd_view_replies() later</li>
102
+ *		<li>load details of all the users applicable for posts in this page</li>
103
+ *		<li>request all the visible attachments from {@link shd_display_load_attachments()}</li>
104
+ *		<li>since we are viewing this ticket, mark it read</li>
105
+ *		<li>set up the breadcrumb trail</li>
106
+ *		<li>set up the ticket navigation menu</li>
107
+ *		<li>call in the editor component from SimpleDesk-Post.php and friends, ready for Quick Reply</li>
108
+ *		<li>invoke the different Javascript objects that are applicable on the page:
109
+ *			<ul>
110
+ *				<li>privacy changer</li>
111
+ *				<li>urgency changer</li>
112
+ *				<li>quick reply / quote / go advanced</li>
113
+ *			</ul>
114
+ *		</li>
115
+ *	</ul>
116
+ *
117
+ *	@see shd_prepare_ticket_context()
118
+ *	@since 1.0
119
+*/
120
+function shd_view_ticket()
121
+{
122
+	global $context, $txt, $scripturl, $settings, $reply_request, $smcFunc, $modSettings, $memberContext, $sourcedir, $user_info, $options;
123
+
124
+	loadTemplate('SimpleDesk-Display');
125
+
126
+	$ticketinfo = shd_load_ticket();
127
+
128
+	// How much are we sticking on each page?
129
+	$context['messages_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) && !WIRELESS ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
130
+
131
+	censorText($ticketinfo['subject']);
132
+	censorText($ticketinfo['body']);
133
+
134
+	$context['user_list'] = array(); // as we go along, build a list of users who are relevant
135
+
136
+	$context['ticket'] = array(
137
+		'id' => $context['ticket_id'],
138
+		'display_id' => str_pad($context['ticket_id'], 5, '0', STR_PAD_LEFT),
139
+		'subject' => $ticketinfo['subject'],
140
+		'first_msg' => $ticketinfo['id_first_msg'],
141
+		'body' => shd_format_text($ticketinfo['body'], $ticketinfo['smileys_enabled'], 'shd_ticket_' . $context['ticket_id']),
142
+		'id_member' => $ticketinfo['id_member'],
143
+		'id_member_assigned' => $ticketinfo['assigned_id'],
144
+		'member' => array(
145
+			'id' => $ticketinfo['starter_id'],
146
+			'name' => $ticketinfo['starter_name'],
147
+			'link' => shd_profile_link($ticketinfo['starter_name'], $ticketinfo['starter_id']),
148
+		),
149
+		'assigned' => array(
150
+			'id' => $ticketinfo['assigned_id'],
151
+			'name' => ($ticketinfo['assigned_id'] > 0) ? $ticketinfo['assigned_name'] : $txt['shd_unassigned'],
152
+			'link' => ($ticketinfo['assigned_id'] > 0) ? shd_profile_link($ticketinfo['assigned_name'], $ticketinfo['assigned_id']) : '<span class="error">' . $txt['shd_unassigned'] . '</span>',
153
+		),
154
+		'assigned_self' => $ticketinfo['assigned_id'] == $user_info['id'],
155
+		'ticket_opener' => $ticketinfo['starter_id'] == $user_info['id'],
156
+		'urgency' => array(
157
+			'level' => $ticketinfo['urgency'],
158
+			'label' => $ticketinfo['urgency'] > TICKET_URGENCY_HIGH ? '<span class="error">' . $txt['shd_urgency_' . $ticketinfo['urgency']] . '</span>' : $txt['shd_urgency_' . $ticketinfo['urgency']],
159
+		),
160
+		'status' => array(
161
+			'level' => $ticketinfo['status'],
162
+			'label' => $txt['shd_status_' . $ticketinfo['status']],
163
+		),
164
+		'num_replies' => $ticketinfo['num_replies'],
165
+		'deleted_replies' => $ticketinfo['deleted_replies'],
166
+		'poster_time' => timeformat($ticketinfo['poster_time']),
167
+		'privacy' => array(
168
+			'label' => $ticketinfo['private'] ? $txt['shd_ticket_private'] : $txt['shd_ticket_notprivate'],
169
+			'can_change' => shd_allowed_to('shd_alter_privacy_any') || (shd_allowed_to('shd_alter_privacy_own') && $ticketinfo['id_member'] == $user_info['id']),
170
+		),
171
+		'closed' => $ticketinfo['closed'],
172
+		'deleted' => $ticketinfo['deleted'],
173
+		'ip_address' => $ticketinfo['link_ip'] ? ('<a href="' . $scripturl . '?action=trackip;searchip=' . $ticketinfo['starter_ip'] . '">' . $ticketinfo['starter_ip'] . '</a>') : $ticketinfo['starter_ip'],
174
+	);
175
+
176
+	// Stuff concerning whether the ticket is deleted or not
177
+	// Display recycling stuff if: ticket is deleted (if we can see it, we can see the bin) OR ticket has deleted replies and we can see the bin and we requested to see them
178
+	$context['ticket']['display_recycle_replies'] = true;
179
+	if ($context['ticket']['deleted'])
180
+		$context['ticket']['display_recycle'] = $txt['shd_ticket_has_been_deleted'];
181
+	elseif ($context['ticket']['deleted_replies'] > 0)
182
+	{
183
+		if (shd_allowed_to('shd_access_recyclebin'))
184
+		{
185
+			$context['ticket']['display_recycle'] = $txt['shd_ticket_replies_deleted'];
186
+			$ticketlink = $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $context['ticket_id'] . (isset($_REQUEST['recycle']) ? '' : ';recycle');
187
+			$context['ticket']['display_recycle'] .= ' ' . sprintf((isset($_REQUEST['recycle']) ? $txt['shd_ticket_replies_deleted_view'] : $txt['shd_ticket_replies_deleted_link']), $ticketlink);
188
+			$context['ticket']['display_recycle_replies'] = (isset($_REQUEST['recycle']));
189
+		}
190
+		else
191
+			$context['ticket']['display_recycle_replies'] = false;
192
+	}
193
+	else
194
+	{
195
+		$context['ticket']['display_recycle'] = false;
196
+		$context['ticket']['display_recycle_replies'] = false;
197
+	}
198
+
199
+	// Ticket privacy
200
+	$context['ticket']['privacy']['can_change'] = $context['ticket']['privacy']['can_change'] && (!$context['ticket']['closed'] && !$context['ticket']['deleted']);
201
+	if (empty($modSettings['shd_privacy_display']) || $modSettings['shd_privacy_display'] == 'smart')
202
+		$context['display_private'] = shd_allowed_to('shd_view_ticket_private_any') || shd_allowed_to('shd_alter_privacy_own') || shd_allowed_to('shd_alter_privacy_any') || $ticketinfo['private'];
203
+	else
204
+		$context['display_private'] = true;
205
+
206
+	$context['link_ip_address'] = $ticketinfo['link_ip'];
207
+
208
+	if ($ticketinfo['modified_time'] > 0)
209
+	{
210
+		$context['ticket']['modified'] = array(
211
+			'id' => $ticketinfo['modified_id'],
212
+			'name' => $ticketinfo['modified_name'],
213
+			'link' => shd_profile_link($ticketinfo['modified_name'], $ticketinfo['modified_id']),
214
+			'timestamp' => $ticketinfo['modified_time'],
215
+			'time' => timeformat($ticketinfo['modified_time']),
216
+		);
217
+	}
218
+
219
+	$context['ticket']['urgency'] += shd_can_alter_urgency($ticketinfo['urgency'], $ticketinfo['starter_id'], $ticketinfo['closed'], $ticketinfo['deleted']);
220
+
221
+	$context['page_index'] = shd_no_expand_pageindex($scripturl . '?action=helpdesk;sa=ticket;ticket=' . $context['ticket_id'] . '.%1$d' . (isset($_REQUEST['recycle']) ? ';recycle' : '') . '#replies', $context['ticket_start'], (empty($context['display_recycle']) ? $context['ticket']['num_replies'] : (int) $context['ticket']['num_replies'] + (int) $context['ticket']['deleted_replies']), $context['messages_per_page'], true);
222
+
223
+	$context['get_replies'] = 'shd_prepare_ticket_context';
224
+
225
+	$query = shd_db_query('', '
226
+		SELECT id_msg, id_member, modified_member
227
+		FROM {db_prefix}helpdesk_ticket_replies
228
+		WHERE id_ticket = {int:ticket}
229
+			AND id_msg > {int:first_msg}' . (!empty($context['ticket']['display_recycle_replies']) ? '' : '
230
+			AND message_status = {int:msg_status}') . '
231
+		ORDER BY id_msg ' . ($context['messages_per_page'] == -1 ? '' : '
232
+		LIMIT ' . $context['ticket_start'] . ', ' . $context['messages_per_page']),
233
+		array(
234
+			'ticket' => $context['ticket_id'],
235
+			'first_msg' => $ticketinfo['id_first_msg'],
236
+			'msg_status' => MSG_STATUS_NORMAL,
237
+		)
238
+	);
239
+
240
+	$context['ticket_messages'] = array();
241
+	$posters = array();
242
+	while ($row = $smcFunc['db_fetch_assoc']($query))
243
+	{
244
+		if (!empty($row['id_member']))
245
+			$posters[] = $row['id_member'];
246
+
247
+		if (!empty($row['modified_member']))
248
+			$posters[] = $row['modified_member'];
249
+
250
+		$context['ticket_messages'][] = $row['id_msg'];
251
+	}
252
+	$smcFunc['db_free_result']($query);
253
+
254
+	// We might want the OP's avatar, add 'em to the list -- just in case.
255
+	$posters[] = $context['ticket']['id_member'];
256
+
257
+	$posters = array_unique($posters);
258
+
259
+	$context['is_team'] = array();
260
+	// Get the poster data
261
+	if (!empty($posters))
262
+	{
263
+		loadMemberData($posters);
264
+
265
+		// Are they current team members?
266
+		$team = array_intersect($posters, shd_members_allowed_to('shd_staff'));
267
+
268
+		foreach ($team as $member)
269
+			$context['is_team'][$member] = true;
270
+	}
271
+
272
+	if (!empty($context['ticket_messages']))
273
+	{
274
+		$reply_request = shd_db_query('', '
275
+			SELECT
276
+				id_msg, poster_time, poster_ip, id_member, modified_time, modified_name, modified_member, body,
277
+				smileys_enabled, poster_name, poster_email, message_status
278
+			FROM {db_prefix}helpdesk_ticket_replies
279
+			WHERE id_msg IN ({array_int:message_list})' . (!empty($context['ticket']['display_recycle']) ? '' : '
280
+				AND message_status IN ({array_int:msg_normal})') . '
281
+			ORDER BY id_msg',
282
+			array(
283
+				'message_list' => $context['ticket_messages'],
284
+				'msg_normal' => array(MSG_STATUS_NORMAL),
285
+			)
286
+		);
287
+	}
288
+	else
289
+	{
290
+		$reply_request = false;
291
+		$context['first_message'] = 0;
292
+		$context['first_new_message'] = false;
293
+	}
294
+
295
+	// Grab the avatar for the poster
296
+	$context['ticket']['poster_avatar'] = empty($context['ticket']['member']['id']) ? array() : (loadMemberContext($context['ticket']['id_member']) ? $memberContext[$context['ticket']['id_member']]['avatar'] : array());
297
+
298
+	// Before we grab attachments, also make sure we get any from the first msg (i.e. the ticket)
299
+	$context['ticket_messages'][] = $context['ticket']['first_msg'];
300
+	shd_display_load_attachments();
301
+
302
+	// Mark read goes here
303
+	if (!empty($user_info['id']))
304
+	{
305
+		$smcFunc['db_insert']('replace',
306
+			'{db_prefix}helpdesk_log_read',
307
+			array(
308
+				'id_ticket' => 'int', 'id_member' => 'int', 'id_msg' => 'int',
309
+			),
310
+			array(
311
+				$context['ticket_id'], $user_info['id'], $ticketinfo['id_last_msg'],
312
+			),
313
+			array('id_member', 'id_topic')
314
+		);
315
+	}
316
+
317
+	// Build the link tree. If the ticket is recycled, display 'Recycle bin', else 'Tickets'.
318
+	$context['linktree'][] = array(
319
+		'url' => $context['ticket']['status']['level'] == TICKET_STATUS_DELETED ? $scripturl . '?action=helpdesk;sa=recyclebin' : $scripturl . '?action=helpdesk;sa=main',
320
+		'name' => $context['ticket']['status']['level'] == TICKET_STATUS_DELETED ? $txt['shd_recycle_bin'] : $txt['shd_linktree_tickets'],
321
+	);
322
+	// If it's closed, add that to the linktree.
323
+	if ($context['ticket']['status']['level'] == TICKET_STATUS_CLOSED)
324
+		$context['linktree'][] = array(
325
+			'url' => $scripturl . '?action=helpdesk;sa=closedtickets',
326
+			'name' => $txt['shd_tickets_closed'],
327
+		);
328
+	// Lastly add the ticket name and link.
329
+	$context['linktree'][] = array(
330
+		'url' => $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $context['ticket_id'],
331
+		'name' => $context['ticket']['subject'],
332
+	);
333
+
334
+	// Template stuff
335
+	$context['sub_template'] = 'viewticket';
336
+	$context['page_title'] = $txt['shd_helpdesk'] . ' [' . $context['ticket']['display_id'] . '] ' . $context['ticket']['subject'];
337
+
338
+	// Ticket navigation
339
+	$context['can_reply'] = !$context['ticket']['closed'] && !$context['ticket']['deleted'] && (shd_allowed_to('shd_reply_ticket_any') || ($context['ticket']['ticket_opener'] && shd_allowed_to('shd_reply_ticket_own'))); // needs perms - calc'd here because we use it in display template too
340
+	$context['can_quote'] = $context['can_reply'] && !empty($modSettings['shd_allow_ticket_bbc']);
341
+	$context['can_go_advanced'] = !empty($modSettings['shd_allow_ticket_bbc']) || !empty($modSettings['allow_ticket_smileys']) || shd_allowed_to('shd_post_attachment');
342
+	$context['can_see_ip'] = shd_allowed_to('shd_staff');
343
+
344
+	$context['ticket_navigation'] = array();
345
+	$context['ticket_navigation'][] = array(
346
+		'url' => $scripturl . '?action=helpdesk;sa=editticket;ticket=' . $context['ticket']['id'] . ';' . $context['session_var'] . '=' . $context['session_id'],
347
+		'icon' => 'edit',
348
+		'alt' => '*',
349
+		'display' => !$context['ticket']['closed'] && !$context['ticket']['deleted'] && (shd_allowed_to('shd_edit_reply_any') || ($context['ticket']['ticket_opener'] && shd_allowed_to('shd_edit_reply_own'))),
350
+		'text' => 'shd_ticket_edit',
351
+	);
352
+	$context['ticket_navigation'][] = array(
353
+		'url' => $scripturl . '?action=helpdesk;sa=markunread;ticket=' . $context['ticket']['id'] . ';' . $context['session_var'] . '=' . $context['session_id'],
354
+		'icon' => 'unread',
355
+		'alt' => '*',
356
+		'display' => !$context['ticket']['closed'] && !$context['ticket']['deleted'],
357
+		'text' => 'shd_ticket_markunread',
358
+	);
359
+	$context['ticket_navigation'][] = array(
360
+		'url' => $scripturl . '?action=helpdesk;sa=resolveticket;ticket=' . $context['ticket']['id'] . ';' . $context['session_var'] . '=' . $context['session_id'],
361
+		'icon' => !$context['ticket']['closed'] ? 'resolved' : 'unresolved',
362
+		'alt' => '*',
363
+		'display' => !$context['ticket']['deleted'] && (shd_allowed_to('shd_resolve_ticket_any') || (shd_allowed_to('shd_resolve_ticket_own') && $context['ticket']['ticket_opener'])),
364
+		'text' => !$context['ticket']['closed'] ? 'shd_ticket_resolved' : 'shd_ticket_unresolved',
365
+	);
366
+
367
+	// This is always going to be a pain. But it should be possible to contextualise it nicely.
368
+	// And while this isn't quite as nicely formatted as a single nice array definition,
369
+	// imagine trying to debug the display and text options later if it were done with nested ternaries... *shudder*
370
+	$assign_nav = array(
371
+		'url' => $scripturl . '?action=helpdesk;sa=assign;ticket=' . $context['ticket']['id'] . ';' . $context['session_var'] . '=' . $context['session_id'],
372
+		'icon' => 'assign',
373
+		'alt' => '*',
374
+		'text' => '',
375
+		'display' => false,
376
+	);
377
+	if (shd_allowed_to('shd_assign_ticket_any'))
378
+	{
379
+		$assign_nav['display'] = true && !$context['ticket']['closed'] && !$context['ticket']['deleted'];
380
+		$assign_nav['text'] = empty($context['ticket']['id_member_assigned']) ? 'shd_ticket_assign' : 'shd_ticket_reassign';
381
+	}
382
+	elseif (shd_allowed_to('shd_assign_ticket_own'))
383
+	{
384
+		$assign_nav['display'] = !$context['ticket']['closed'] && !$context['ticket']['deleted'] && (empty($context['ticket']['id_member_assigned']) || $context['ticket']['assigned_self']); // either not assigned or assigned to self
385
+		$assign_nav['text'] = $context['ticket']['assigned_self'] ? 'shd_ticket_unassign' : 'shd_ticket_assign_self';
386
+	}
387
+
388
+	$context['ticket_navigation'][] = $assign_nav;
389
+
390
+	$context['ticket_navigation'][] = array(
391
+		'url' => $scripturl . '?action=helpdesk;sa=deleteticket;ticket=' . $context['ticket']['id'] . ';' . $context['session_var'] . '=' . $context['session_id'],
392
+		'icon' => 'delete',
393
+		'alt' => '*',
394
+		'display' => !$context['ticket']['closed'] && !$context['ticket']['deleted'] && (shd_allowed_to('shd_delete_ticket_any') || (shd_allowed_to('shd_delete_ticket_own') && $context['ticket']['ticket_opener'])),
395
+		'text' => 'shd_ticket_delete',
396
+		'onclick' => 'return confirm(' . JavaScriptEscape($txt['shd_delete_confirm']) . ');',
397
+	);
398
+	$context['ticket_navigation'][] = array(
399
+		'url' => $scripturl . '?action=helpdesk;sa=restoreticket;ticket=' . $context['ticket']['id'] . ';' . $context['session_var'] . '=' . $context['session_id'],
400
+		'icon' => 'restore',
401
+		'alt' => '*',
402
+		'display' => $context['ticket']['deleted'] && (shd_allowed_to('shd_restore_ticket_any') || (shd_allowed_to('shd_restore_ticket_own') && $context['ticket']['ticket_opener'])),
403
+		'text' => 'shd_ticket_restore',
404
+	);
405
+	$context['ticket_navigation'][] = array(
406
+		'url' => $scripturl . '?action=helpdesk;sa=permadelete;ticket=' . $context['ticket']['id'] . ';' . $context['session_var'] . '=' . $context['session_id'],
407
+		'icon' => 'delete',
408
+		'alt' => '*',
409
+		'display' => $context['ticket']['deleted'] && shd_allowed_to('shd_delete_recycling'),
410
+		'text' => 'shd_delete_permanently',
411
+		'onclick' => 'return confirm(' . JavaScriptEscape($txt['shd_delete_permanently_confirm']) . ');',
412
+	);
413
+
414
+	$context['shd_can_move_to_topic'] = shd_allowed_to('shd_ticket_to_topic') && empty($modSettings['shd_helpdesk_only']);
415
+	$context['ticket_navigation'][] = array(
416
+		'url' => $scripturl . '?action=helpdesk;sa=tickettotopic;ticket=' . $context['ticket']['id'] . ';' . $context['session_var'] . '=' . $context['session_id'],
417
+		'icon' => 'tickettotopic',
418
+		'alt' => '*',
419
+		'display' => $context['shd_can_move_to_topic'] && !$context['ticket']['closed'] && !$context['ticket']['deleted'] && ($context['ticket']['deleted_replies'] == 0 || shd_allowed_to('shd_access_recyclebin')),
420
+		'text' => 'shd_ticket_move_to_topic',
421
+	);
422
+
423
+	// If we are going SMF style with the navigation, we need to rework the structure a wee bit.
424
+	// No sense making a new array, mind, just fix up the existing one a touch, and don't do this on the master as we don't always need it.
425
+	if (empty($modSettings['shd_ticketnav_style']) || !in_array($modSettings['shd_ticketnav_style'], array('sd', 'sdcompact', 'smf')))
426
+		$modSettings['shd_ticketnav_style'] = 'sd';
427
+
428
+	if ($modSettings['shd_ticketnav_style'] == 'smf')
429
+		foreach ($context['ticket_navigation'] as $key => $button)
430
+		{
431
+			$context['can_' . $button['text']] = $button['display'];
432
+			$context['ticket_navigation'][$key] += array(
433
+				'lang' => true,
434
+				'test' => 'can_' . $button['text'],
435
+				'image' => 'shd_ticket_' . $button['icon'] . '.png',
436
+			);
437
+		}
438
+
439
+	if (empty($modSettings['shd_staff_badge']))
440
+		$modSettings['shd_staff_badge'] = 'nobadge';
441
+
442
+	// Quick reply stuffs
443
+	require_once($sourcedir . '/SimpleDesk-Post.php');
444
+	require_once($sourcedir . '/Subs-Editor.php');
445
+	loadTemplate('SimpleDesk-Post');
446
+
447
+	if (empty($modSettings['shd_attachments_mode']))
448
+		$modSettings['shd_attachments_mode'] = 'ticket';
449
+
450
+	$context['ticket_form']['ticket'] = $context['ticket_id'];
451
+	$context['ticket_form']['num_allowed_attachments'] = empty($modSettings['attachmentNumPerPostLimit']) || $modSettings['shd_attachments_mode'] == 'ticket' ? -1 : $modSettings['attachmentNumPerPostLimit'];
452
+	$context['ticket_form']['do_attach'] = shd_allowed_to('shd_post_attachment');
453
+	$context['ticket_form']['num_replies'] = $context['ticket']['num_replies'];
454
+	$context['ticket_form']['disable_smileys'] = empty($modSettings['shd_allow_ticket_smileys']);
455
+
456
+	// Set up the fancy editor
457
+	shd_postbox(
458
+		'shd_message',
459
+		'',
460
+		array(
461
+			'post_button' => $txt['shd_reply_ticket'],
462
+		)
463
+	);
464
+
465
+	// Lastly, our magic AJAX stuff ;D and we know we already made html_headers exist in SimpleDesk.php, score!
466
+	$context['html_headers'] .= '
467
+	<script type="text/javascript"><!-- // --><![CDATA[
468
+	var sSessI = "' . $context['session_id'] . '";
469
+	var sSessV = "' . $context['session_var'] . '";';
470
+
471
+	if ($context['ticket']['privacy']['can_change'])
472
+		$context['html_headers'] .= '
473
+	var shd_ajax_problem = ' . JavaScriptEscape($txt['shd_ajax_problem']) . ';
474
+	var privacyCtl = new shd_privacyControl({
475
+		ticket: ' . $context['ticket_id'] . ',
476
+		sUrl: smf_scripturl + "?action=helpdesk;sa=ajax;op=privacy;ticket=' . $context['ticket_id'] . '",
477
+		sSession: sSessV + "=" + sSessI,
478
+		sSrcA: "privlink",
479
+		sDestSpan: "privacy"
480
+	});';
481
+
482
+	if ($context['ticket']['urgency']['increase'] || $context['ticket']['urgency']['decrease'])
483
+		$context['html_headers'] .= '
484
+	var urgencyCtl = new shd_urgencyControl({
485
+		ticket: ' . $context['ticket_id'] . ',
486
+		sUrl: smf_scripturl + "?action=helpdesk;sa=ajax;op=urgency;ticket=' . $context['ticket_id'] . ';change=",
487
+		sSession: sSessV + "=" + sSessI,
488
+		sDestSpan: "urgency",
489
+		aButtons: ["up", "down"],
490
+		aButtonOps: { up: "increase", down: "decrease" }
491
+	});';
492
+
493
+	if (!empty($options['display_quick_reply']))
494
+		$context['html_headers'] .= '
495
+	var oQuickReply = new QuickReply({
496
+		bDefaultCollapsed: ' . (!empty($options['display_quick_reply']) && $options['display_quick_reply'] == 2 ? 'false' : 'true') .  ',
497
+		iTicketId: ' . $context['ticket_id'] . ',
498
+		iStart: ' . $context['start'] . ',
499
+		sScriptUrl: smf_scripturl,
500
+		sImagesUrl: "' . $settings['images_url'] . '",
501
+		sContainerId: "quickReplyOptions",
502
+		sImageId: "quickReplyExpand",
503
+		sImageCollapsed: "collapse.gif",
504
+		sImageExpanded: "expand.gif",
505
+		sJumpAnchor: "quickreply",
506
+		sHeaderId: "quickreplyheader",
507
+		sFooterId: "quickreplyfooter"
508
+	});';
509
+
510
+	if (!empty($options['display_quick_reply']) && $context['can_go_advanced'])
511
+		$context['html_headers'] .= '
512
+	function goAdvanced()
513
+	{
514
+		document.getElementById("shd_bbcbox").style.display = ' . (!empty($modSettings['shd_allow_ticket_bbc']) ? '""' : '"none"') . ';
515
+		document.getElementById("shd_smileybox").style.display = ' . (!empty($modSettings['shd_allow_ticket_smileys']) ? '""' : '"none"') . ';
516
+		document.getElementById("shd_attach_container").style.display = ' . (!empty($context['ticket_form']['do_attach']) ? '""' : '"none"') . ';
517
+		document.getElementById("shd_goadvancedbutton").style.display = "none";' . (!empty($context['controls']['richedit']['shd_message']['rich_active']) ? '
518
+		oEditorHandle_shd_message.toggleView(true);' : '') . '
519
+	}
520
+	';
521
+
522
+	$context['html_headers'] .= '
523
+	// ]' . ']></script>';
524
+
525
+	$context['shd_display'] = true;
526
+	$context['controls']['richedit']['shd_message']['rich_active'] = 0; // we don't want it by default!
527
+
528
+	// Register this form in the session variables.
529
+	checkSubmitOnce('register');
530
+}
531
+
532
+/**
533
+ *	Callback function for the template to load messages.
534
+ *
535
+ *	The process set up by shd_view_ticket() and invoked within template_view_replies() is reasonably complex.
536
+ *	{@link shd_view_ticket()} identifies what messages should be displayed on the current page of the ticket, and performs a query
537
+ *	to load the ticket data. Instead, however, of retrieving every row directly into memory before passing to the template,
538
+ *	it passes the query result, and the name of a handler function into $context, so the template can call to get an
539
+ *	individual row at a time, which saves memory amongst other things.
540
+ *
541
+ *	With respect to {@link shd_view_ticket()}, the relevant items are $reply_request being defined and $context['get_replies']
542
+ *	being defined as the name of this function, and in {@link template_view_replies()}, the reference is $reply = $context['get_replies']()
543
+ *
544
+ *	@return mixed The function returns the "next" message reply's details, or simply false if no replies were available, or no further replies are available. Assuming a reply can be returned, it will be a hash array in the following format:
545
+ *	<ul>
546
+ *		<li>id: numeric message id</li>
547
+ *		<li>member: hash array containing details of the poster; normally the return value from SMF's loadMemberContext() function. A minimal set of details is prepared if the poster holds no current SMF account. Common values:
548
+ *			<ul>
549
+ *				<li>name: User's name (falls back to the poster name specified in the replies table)</li>
550
+ *				<li>id: User's id</li>
551
+ *				<li>group: Name of the assigned group/post count group of the user</li>
552
+ *				<li>link: HTML for a hyperlink to their profile</li>
553
+ *				<li>email: Email address of the poster</li>
554
+ *				<li>ip: IP address of the poster</li>
555
+ *			</ul>
556
+ *		</li>
557
+ *		<li>body: censored, parsed for smileys and bbcode (in {@link shd_format_text()})</li>
558
+ *		<li>time: string of the time the reply was posted</li>
559
+ *		<li>timestamp: internal stored timestamp attached to the reply</li>
560
+ *		<li>is_team: boolean value of whether the posting member is currently helpdesk staff</li>
561
+ *		<li>can_edit: boolean value reflecting if this reply can be edited</li>
562
+ *		<li>can_delete: boolean value reflecting if this reply can be deleted</li>
563
+ *		<li>can_restore: boolean value reflecting if this reply can be restored</li>
564
+ *		<li>ip_address: IP address used to post the message (not necessarily the user's normal IP address); if the user has moderate_forum_members permission, this returns a link to the track IP area, with the IP address as the link text, alternatively simply the IP address if not (is only displayed to helpdesk staff)</li>
565
+ *		<li>modified: may not be declared, if it is, the message was modified some time after posting, and the following data items are in the hash array within:
566
+ *			<ul>
567
+ *				<li>id: user id who edited the reply (not always available)</li>
568
+ *				<li>time: formatted string of the time the post was edited</li>
569
+ *				<li>timestamp: raw timestamp of the time the post was edited</li>
570
+ *				<li>name: user name of the editing user; if we have a definite user id, this should contain the current name, falling back to the previously stored name</li>
571
+ *				<li>link: if we have a known, valid user id for the post's editor, this will contain a link to their profile, with the link text using their current display name; alternatively it will contain a regular string which is the username stored with the edit.</li>
572
+ *			</ul>
573
+ *		</li>
574
+ *	</ul>
575
+ *
576
+ *	@see shd_view_ticket()
577
+ *	@since 1.0
578
+*/
579
+function shd_prepare_ticket_context()
580
+{
581
+	global $settings, $txt, $modSettings, $scripturl, $options, $user_info, $smcFunc;
582
+	global $memberContext, $context, $reply_request, $user_profile;
583
+
584
+	if (empty($reply_request))
585
+		return false;
586
+
587
+	$message = $smcFunc['db_fetch_assoc']($reply_request);
588
+	if (!$message)
589
+	{
590
+		$smcFunc['db_free_result']($reply_request);
591
+		return false;
592
+	}
593
+
594
+	if (!loadMemberContext($message['id_member'], true))
595
+	{
596
+		// Notice this information isn't used anywhere else....
597
+		$memberContext[$message['id_member']]['name'] = $message['poster_name'];
598
+		$memberContext[$message['id_member']]['id'] = 0;
599
+		$memberContext[$message['id_member']]['group'] = $txt['guest_title'];
600
+		$memberContext[$message['id_member']]['link'] = $message['poster_name'];
601
+		$memberContext[$message['id_member']]['email'] = $message['poster_email'];
602
+		$memberContext[$message['id_member']]['show_email'] = showEmailAddress(true, 0);
603
+		$memberContext[$message['id_member']]['is_guest'] = true;
604
+		$memberContext[$message['id_member']]['group_stars'] = '';
605
+	}
606
+	$memberContext[$message['id_member']]['ip'] = $message['poster_ip'];
607
+
608
+	censorText($message['body']);
609
+	$message['body'] = shd_format_text($message['body'], $message['smileys_enabled'], 'shd_reply_' . $message['id_msg']);
610
+
611
+	$output = array(
612
+		'id' => $message['id_msg'],
613
+		'member' => &$memberContext[$message['id_member']],
614
+		'time' => timeformat($message['poster_time']),
615
+		'timestamp' => forum_time(true, $message['poster_time']),
616
+		'body' => $message['body'],
617
+		'is_team' => !empty($context['is_team'][$message['id_member']]),
618
+		'can_edit' => $message['message_status'] != MSG_STATUS_DELETED && !$context['ticket']['closed'] && !$context['ticket']['deleted'] && (shd_allowed_to('shd_edit_reply_any') || ($message['id_member'] == $user_info['id'] && shd_allowed_to('shd_edit_reply_own'))),
619
+		'can_delete' => $message['message_status'] != MSG_STATUS_DELETED && !$context['ticket']['closed'] && !$context['ticket']['deleted'] && (shd_allowed_to('shd_delete_reply_any') || ($message['id_member'] == $user_info['id'] && shd_allowed_to('shd_delete_reply_own'))),
620
+		'can_restore' => $message['message_status'] == MSG_STATUS_DELETED && !$context['ticket']['closed'] && !$context['ticket']['deleted'] && (shd_allowed_to('shd_restore_reply_any') || ($message['id_member'] == $user_info['id'] && shd_allowed_to('shd_restore_reply_own'))),
621
+		'can_permadelete' => $message['message_status'] == MSG_STATUS_DELETED && !$context['ticket']['closed'] && !$context['ticket']['deleted'] && shd_allowed_to('shd_delete_recycling'),
622
+		'ip_address' => $context['link_ip_address'] ? ('<a href="' . $scripturl . '?action=trackip;searchip=' . $message['poster_ip'] . '">' . $message['poster_ip'] . '</a>') : $message['poster_ip'],
623
+		'message_status' => $message['message_status'],
624
+	);
625
+
626
+	if (!empty($message['modified_time']))
627
+	{
628
+		$output['modified'] = array(
629
+			'time' => timeformat($message['modified_time']),
630
+			'timestamp' => forum_time(true, $message['modified_time']),
631
+			'id' => !empty($user_profile[$message['modified_member']]) ? $message['modified_member'] : 0,
632
+			'name' => !empty($user_profile[$message['modified_member']]) ? $user_profile[$message['modified_member']]['real_name'] : $message['modified_name'],
633
+		);
634
+		$output['modified']['link'] = shd_profile_link($output['modified']['name'], $output['modified']['id']);
635
+	}
636
+
637
+	return $output;
638
+}
639
+
640
+/**
641
+ *	Loads data on all the attachments that will be displayed in a ticket's view.
642
+ *
643
+ *	Queries the {db_prefix}helpdesk_attachments table for the current ticket, or the messages that will be displayed
644
+ *	(previously identified by {@link shd_view_ticket()}) and loads the id, name and size of them for display purposes. Differentiation
645
+ *	between "attach to ticket" and "attach to reply" is made here; the data is loaded differently depending on the context;
646
+ *	"attach to ticket" is simply loaded into an indexed array stored in $context['ticket_attach']['ticket'], while "attach
647
+ *	to reply" is loaded into an indexed array subset in $context['ticket_attach']['reply'][msg_id] to retain the association.
648
+ *
649
+ *	@return array Builds an array within $contxt['ticket_attach'], as noted above. The individual data items per attachment are:
650
+ *	- id: Numeric id for attachment itself
651
+ *	- name: HTML sanitised name of the attachment's filename
652
+ *	- size: string listing the size of the attachment, converted to kilobytes and rounded to two decimal places
653
+ *	- byte_size: integer of raw size of attachment
654
+ *	- href: the URL that would be used to access the attachment (required $context['ticket_id'] to be available, which it would anyway)
655
+ *	- link: full HTML "a" element linking to the attachment, using the filename as the link text
656
+ *	@since 1.0
657
+*/
658
+function shd_display_load_attachments()
659
+{
660
+	global $modSettings, $context, $smcFunc, $scripturl, $txt;
661
+
662
+	if (empty($modSettings['shd_attachments_mode']))
663
+		$modSettings['shd_attachments_mode'] = 'ticket'; // just in case we never actually go to the admin page!!
664
+
665
+	$context['ticket_attach'][$modSettings['shd_attachments_mode']] = array();
666
+
667
+	if ($modSettings['shd_attachments_mode'] == 'ticket')
668
+	{
669
+		$query = shd_db_query('', '
670
+			SELECT hda.id_attach, a.filename, a.size
671
+			FROM {db_prefix}helpdesk_attachments AS hda
672
+				INNER JOIN {db_prefix}attachments AS a ON (hda.id_attach = a.id_attach)
673
+			WHERE hda.id_ticket = {int:ticket}
674
+				AND a.attachment_type != {int:thumb}
675
+			ORDER BY hda.id_attach',
676
+			array(
677
+				'ticket' => $context['ticket_id'],
678
+				'thumb' => 3,
679
+			)
680
+		);
681
+
682
+		// Ticket ones can just be added on, they're nice and straightforward
683
+		while ($row = $smcFunc['db_fetch_assoc']($query))
684
+			$context['ticket_attach'][$modSettings['shd_attachments_mode']][] = array(
685
+				'id' => $row['id_attach'],
686
+				'name' => preg_replace('~&amp;#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', htmlspecialchars($row['filename'])),
687
+				'size' => round($row['size'] / 1024, 2) . ' ' . $txt['kilobyte'],
688
+				'byte_size' => $row['size'],
689
+				'href' => $scripturl . '?action=dlattach;ticket=' . $context['ticket_id'] . '.0;attach=' . $row['id_attach'],
690
+				'link' => '<a href="' . $scripturl . '?action=dlattach;ticket=' . $context['ticket_id'] . '.0;attach=' . $row['id_attach'] . '">' . htmlspecialchars($row['filename']) . '</a>',
691
+			);
692
+	}
693
+	else
694
+	{
695
+		if (empty($context['ticket_messages']))
696
+			return;
697
+
698
+		$query = shd_db_query('', '
699
+			SELECT hda.id_attach, hda.id_msg, a.filename, a.size
700
+			FROM {db_prefix}helpdesk_attachments AS hda
701
+				INNER JOIN {db_prefix}attachments AS a ON (hda.id_attach = a.id_attach)
702
+			WHERE hda.id_msg IN ({array_int:msg})
703
+				AND a.attachment_type != {int:thumb}
704
+			ORDER BY hda.id_attach',
705
+			array(
706
+				'msg' => $context['ticket_messages'],
707
+				'thumb' => 3,
708
+			)
709
+		);
710
+
711
+		// Message ones are a little trickier since we actually need to tie them to msg ids
712
+		while ($row = $smcFunc['db_fetch_assoc']($query))
713
+			$context['ticket_attach'][$modSettings['shd_attachments_mode']][$row['id_msg']][] = array(
714
+				'id' => $row['id_attach'],
715
+				'name' => preg_replace('~&amp;#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', htmlspecialchars($row['filename'])),
716
+				'size' => round($row['size'] / 1024, 2) . ' ' . $txt['kilobyte'],
717
+				'byte_size' => $row['size'],
718
+				'href' => $scripturl . '?action=dlattach;ticket=' . $context['ticket_id'] . '.0;attach=' . $row['id_attach'],
719
+				'link' => '<a href="' . $scripturl . '?action=dlattach;ticket=' . $context['ticket_id'] . '.0;attach=' . $row['id_attach'] . '">' . htmlspecialchars($row['filename']) . '</a>',
720
+			);
721
+	}
722
+
723
+	$smcFunc['db_free_result']($query);
724
+}
725
+?>
0 726
\ No newline at end of file
... ...
@@ -0,0 +1,270 @@
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: 1.0 Felidae                             #
17
+# File Info: SimpleDesk-MiscActions.php / 1.0 Felidae         #
18
+###############################################################
19
+
20
+/**
21
+ *	This file handles miscellaneous actions that aren't really tied to anything, that are mostly
22
+ *	self-contained and aren't big enough to warrant their own file.
23
+ *
24
+ *	@package source
25
+ *	@since 1.0
26
+ */
27
+
28
+if (!defined('SMF'))
29
+	die('Hacking attempt...');
30
+
31
+/**
32
+ *	Marks a ticket unread.
33
+ *
34
+ *	There are no permission checks made; other than that the user is who they claim to be. If a ticket is marked unread
35
+ *	but they can't see it anyway, the consequence is that the database gets lighter.
36
+ *
37
+ *	Invoked through ?action=helpdesk;sa=unreadticket;ticket=x;sessvar=sessid before redirecting back to the main helpdesk page.
38
+ *
39
+ *	@since 1.0
40
+*/
41
+function shd_ticket_unread()
42
+{
43
+	global $smcFunc, $user_info, $context;
44
+
45
+	is_not_guest();
46
+	checkSession('get');
47
+
48
+	if (!empty($context['ticket_id']))
49
+		$result = shd_db_query('', '
50
+			DELETE FROM {db_prefix}helpdesk_log_read
51
+			WHERE id_ticket = {int:current_ticket}
52
+				AND id_member = {int:user}',
53
+			array(
54
+				'current_ticket' => $context['ticket_id'],
55
+				'user' => $user_info['id'],
56
+			)
57
+		);
58
+
59
+	redirectexit('action=helpdesk;sa=main');
60
+}
61
+
62
+/**
63
+ *	Marks a ticket resolved or unresolved.
64
+ *
65
+ *	This function identifies whether a given ticket is resolved or not, if not resolved, mark it resolved. If it was resolved
66
+ *	reopen the ticket back to an appropriate status based on last respondent. In both cases, the action is logged in the action log.
67
+ *	It is also unassigned from having a user on either closure or reopen.
68
+ *
69
+ *	Accessed through ?action=helpdesk;sa=resolve;ticket=x;sessvar=sessid before redirecting back to the ticket, or add ;home to the
70
+ *	URL to have it redirect back to the home page.
71
+ *
72
+ *	@since 1.0
73
+*/
74
+function shd_ticket_resolve()
75
+{
76
+	global $smcFunc, $user_info, $context, $sourcedir;
77
+
78
+	checkSession('get');
79
+
80
+	// First, figure out the state of the ticket - is it resolved or not? Can we even see it?
81
+	if (empty($context['ticket_id']))
82
+		fatal_lang_error('shd_no_ticket', false);
83
+
84
+	$context['shd_return_to'] = isset($_REQUEST['home']) ? 'home' : 'ticket';
85
+
86
+	$query = shd_db_query('', '
87
+		SELECT id_member_started, id_member_updated, status, num_replies, subject
88
+		FROM {db_prefix}helpdesk_tickets AS hdt
89
+		WHERE {query_see_ticket}
90
+			AND id_ticket = {int:current_ticket}',
91
+		array(
92
+			'current_ticket' => $context['ticket_id'],
93
+		)
94
+	);
95
+
96
+	if ($row = $smcFunc['db_fetch_assoc']($query))
97
+	{
98
+		$smcFunc['db_free_result']($query);
99
+
100
+		if (!shd_allowed_to('shd_resolve_ticket_any') && (!shd_allowed_to('shd_resolve_ticket_own') || $row['id_member_started'] != $user_info['id']))
101
+			fatal_lang_error('shd_cannot_resolve', false);
102
+
103
+		$action = ($row['status'] != TICKET_STATUS_CLOSED) ? 'resolve' : 'unresolve';
104
+		$new = shd_determine_status($action, $row['id_member_started'], $row['id_member_updated'], $row['num_replies']);
105
+
106
+		shd_db_query('', '
107
+			UPDATE {db_prefix}helpdesk_tickets
108
+			SET status = {int:status},
109
+				id_member_assigned = {int:unassigned}
110
+			WHERE id_ticket = {int:ticket}',
111
+			array(
112
+				'status' => $new,
113
+				'unassigned' => 0,
114
+				'ticket' => $context['ticket_id'],
115
+			)
116
+		);
117
+
118
+		shd_log_action($action,
119
+			array(
120
+				'ticket' => $context['ticket_id'],
121
+				'subject' => $row['subject'],
122
+			)
123
+		);
124
+
125
+		// Make sure we recalculate the number of tickets on next page load (here it will affect visibility)
126
+		shd_clear_active_tickets($row['id_member_started']);
127
+
128
+		if ($context['shd_return_to'] == 'home')
129
+			redirectexit('action=helpdesk;sa=main');
130
+		else
131
+			redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id']);
132
+	}
133
+	else
134
+		fatal_lang_error('shd_no_ticket', false);
135
+}
136
+
137
+/**
138
+ *	Marks a ticket private/not private if the user isn't using Javascript.
139
+ *
140
+ *	Assuming the user can see the ticket and has suitable permissions, the privacy flag will be inverted for the ticket and
141
+ *	updated, as well as updating the action log. If they cannot see the ticket, or do not have privacy-change permission, a fatal
142
+ *	error will be generated.
143
+ *
144
+ *	Accessed through ?action=helpdesk;sa=privacychange;ticket=x;sessvar=sessid before directing back to the main helpdesk page.
145
+ *
146
+ *	@since 1.0
147
+*/
148
+function shd_privacy_change_noajax()
149
+{
150
+	global $smcFunc, $user_info, $context, $sourcedir;
151
+
152
+	checkSession('get');
153
+
154
+	// First, figure out the state of the ticket - is it private or not? Can we even see it?
155
+	if (empty($context['ticket_id']))
156
+		fatal_lang_error('shd_no_ticket', false);
157
+
158
+	$query = shd_db_query('', '
159
+		SELECT id_member_started, subject, private, status
160
+		FROM {db_prefix}helpdesk_tickets AS hdt
161
+		WHERE {query_see_ticket}
162
+			AND id_ticket = {int:current_ticket}',
163
+		array(
164
+			'current_ticket' => $context['ticket_id'],
165
+		)
166
+	);
167
+
168
+	if ($row = $smcFunc['db_fetch_assoc']($query))
169
+	{
170
+		if (in_array($row['status'], array(TICKET_STATUS_CLOSED, TICKET_STATUS_DELETED)) || !shd_allowed_to('shd_alter_privacy_any') && (!shd_allowed_to('shd_alter_privacy_own') || $row['id_member_started'] != $user_info['id']))
171
+			fatal_lang_error('shd_cannot_change_privacy', false);
172
+
173
+		$smcFunc['db_free_result']($query);
174
+
175
+		$new = empty($row['private']) ? 1 : 0;
176
+		$action = empty($row['private']) ? 'markprivate' : 'marknotprivate';
177
+
178
+		require_once($sourcedir . '/Subs-SimpleDeskPost.php');
179
+		$msgOptions = array();
180
+		$posterOptions = array();
181
+		$ticketOptions = array(
182
+			'id' => $context['ticket_id'],
183
+			'private' => empty($row['private']),
184
+		);
185
+
186
+		shd_modify_ticket_post($msgOptions, $ticketOptions, $posterOptions);
187
+
188
+		shd_log_action($action,
189
+			array(
190
+				'ticket' => $context['ticket_id'],
191
+				'subject' => $row['subject'],
192
+			)
193
+		);
194
+
195
+		// Make sure we recalculate the number of tickets on next page load (here it may affect ticket visibility so we do it)
196
+		shd_clear_active_tickets($row['id_member_started']);
197
+
198
+		redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id']);
199
+	}
200
+	else
201
+		fatal_lang_error('shd_no_ticket', false);
202
+}
203
+
204
+/**
205
+ *	Updates a ticket's urgency up or down one level, if the user isn't using Javascript
206
+ *
207
+ *	Assuming the user can see the ticket and has suitable permissions, the urgency of the ticket will be updated and an
208
+ *	action log entry added. If they cannot see the ticket, or do not have urgency-change permission (including higher
209
+ *	urgency permission if necessary), a fatal error will be generated.
210
+ *
211
+ *	Accessed through ?action=helpdesk;sa=urgencychange;ticket=x;change=(increase|decrease);sessvar=sessid before directing back to the main helpdesk page.
212
+ *
213
+ *	@see shd_can_alter_urgency()
214
+ *
215
+ *	@since 1.0
216
+*/
217
+function shd_urgency_change_noajax()
218
+{
219
+	global $smcFunc, $user_info, $context, $sourcedir;
220
+
221
+	checkSession('get');
222
+
223
+	// First, figure out the state of the ticket - is it private or not? Can we even see it? Current urgency?
224
+	if (empty($context['ticket_id']))
225
+		fatal_lang_error('shd_no_ticket', false);
226
+
227
+	$query = shd_db_query('', '
228
+		SELECT id_member_started, subject, urgency, status
229
+		FROM {db_prefix}helpdesk_tickets AS hdt
230
+		WHERE {query_see_ticket}
231
+			AND id_ticket = {int:current_ticket}',
232
+		array(
233
+			'current_ticket' => $context['ticket_id'],
234
+		)
235
+	);
236
+
237
+	if ($row = $smcFunc['db_fetch_assoc']($query))
238
+	{
239
+		$can_urgency = shd_can_alter_urgency($row['urgency'], $row['id_member_started'], ($row['status'] == TICKET_STATUS_CLOSED), ($row['status'] == TICKET_STATUS_DELETED));
240
+
241
+		if (empty($_GET['change']) || empty($can_urgency[$_GET['change']]))
242
+			fatal_lang_error('shd_cannot_change_urgency');
243
+
244
+		$new_urgency = $row['urgency'] + ($_GET['change'] == 'increase' ? 1 : -1);
245
+		$action = 'urgency_' . $_GET['change'];
246
+
247
+		require_once($sourcedir . '/Subs-SimpleDeskPost.php');
248
+		$msgOptions = array();
249
+		$posterOptions = array();
250
+		$ticketOptions = array(
251
+			'id' => $context['ticket_id'],
252
+			'private' => empty($row['private']),
253
+		);
254
+
255
+		shd_modify_ticket_post($msgOptions, $ticketOptions, $posterOptions);
256
+
257
+		shd_log_action($action,
258
+			array(
259
+				'ticket' => $context['ticket_id'],
260
+				'subject' => $row['subject'],
261
+				'urgency' => $new_urgency,
262
+			)
263
+		);
264
+
265
+		redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id']);
266
+	}
267
+	else
268
+		fatal_lang_error('shd_no_ticket', false);
269
+}
270
+?>
0 271
\ No newline at end of file
... ...
@@ -0,0 +1,1668 @@
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: 1.0 Felidae                             #
17
+# File Info: SimpleDesk-Post.php / 1.0 Felidae                #
18
+###############################################################
19
+
20
+/**
21
+ *	This file is one of the cornerstones of SimpleDesk; it handles displaying the post form to users, both for tickets and replies,
22
+ *	handles editing of tickets/replies, attachments to tickets/replies and actually saving all that into the database.
23
+ *
24
+ *	@package source
25
+ *	@todo Finish documenting this file.
26
+ *	@since 1.0
27
+*/
28
+
29
+if (!defined('SMF'))
30
+	die('Hacking attempt...');
31
+
32
+/**
33
+ *	Create a new ticket
34
+*/
35
+function shd_post_ticket()
36
+{
37
+	global $context, $user_info, $sourcedir, $txt, $scripturl, $reply_request, $smcFunc, $options, $memberContext;
38
+	$context['tabindex'] = 1;
39
+
40
+	$new_ticket = $_REQUEST['sa'] == 'newticket';
41
+
42
+	if ($new_ticket)
43
+		shd_is_allowed_to('shd_new_ticket');
44
+	else
45
+	{
46
+		checkSession('get');
47
+		$ticketinfo = shd_load_ticket();
48
+		if (!shd_allowed_to('shd_edit_ticket_any') && (!shd_allowed_to('shd_edit_ticket_own') || !$ticketinfo['is_own']))
49
+			fatal_lang_error('cannot_shd_edit_ticket');
50
+	}
51
+
52
+	// Things we need
53
+	loadTemplate('SimpleDesk-Post');
54
+	require_once($sourcedir . '/Subs-SimpleDeskPost.php');
55
+	require_once($sourcedir . '/Subs-Post.php');
56
+	require_once($sourcedir . '/Subs-Editor.php');
57
+
58
+	if (empty($modSettings['shd_attachments_mode']))
59
+		$modSettings['shd_attachments_mode'] = 'ticket';
60
+
61
+	$context['ticket_form'] = array( // yes, everything goes in here.
62
+		'form_title' => $new_ticket ? $txt['shd_create_ticket'] : $txt['shd_edit_ticket'],
63
+		'form_action' => $scripturl . '?action=helpdesk;sa=saveticket',
64
+		'first_msg' => $new_ticket ? 0 : $ticketinfo['id_first_msg'],
65
+		'message' => $new_ticket ? '' : $ticketinfo['body'],
66
+		'subject' => $new_ticket ? '' : $ticketinfo['subject'],
67
+		'ticket' => $new_ticket ? 0 : $context['ticket_id'],
68
+		'link' => $new_ticket ? '' : '<a href="' . $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $context['ticket_id'] . '">' . $ticketinfo['subject'] . '</a>',
69
+		'msg' => $new_ticket ? 0 : $ticketinfo['id_first_msg'],
70
+		'display_id' => $new_ticket ? '' : str_pad($context['ticket_id'], 5, '0', STR_PAD_LEFT),
71
+		'status' => $new_ticket ? TICKET_STATUS_NEW : $ticketinfo['status'],
72
+		'urgency' => array(
73
+			'setting' => $new_ticket ? TICKET_URGENCY_LOW : $ticketinfo['urgency'],
74
+		),
75
+		'private' => array(
76
+			'setting' => $new_ticket ? false : ($ticketinfo['private'] == 1),
77
+			'can_change' => shd_allowed_to('shd_alter_privacy_any') || (shd_allowed_to('shd_alter_privacy_own') && ($new_ticket || !empty($ticketinfo['is_own']))),
78
+			'options' => array(
79
+				0 => 'shd_ticket_notprivate',
80
+				1 => 'shd_ticket_private',
81
+			),
82
+		),
83
+		'errors' => array(),
84
+		'num_replies' => $new_ticket ? 0 : $ticketinfo['num_replies'],
85
+		'do_attach' => shd_allowed_to('shd_post_attachment'),
86
+		'num_allowed_attachments' => empty($modSettings['attachmentNumPerPostLimit']) || $modSettings['shd_attachments_mode'] == 'ticket' ? -1 : $modSettings['attachmentNumPerPostLimit'],
87
+		'return_to_ticket' => isset($_REQUEST['goback']),
88
+		'disable_smileys' => $new_ticket ? !empty($_REQUEST['no_smileys']) : ($ticketinfo['smileys_enabled'] == 0),
89
+	);
90
+
91
+	// A few basic checks
92
+	if ($context['ticket_form']['status'] == TICKET_STATUS_CLOSED)
93
+		fatal_lang_error('shd_cannot_edit_closed', false);
94
+	elseif ($context['ticket_form']['status'] == TICKET_STATUS_DELETED)
95
+		fatal_lang_error('shd_cannon_edit_deleted', false);
96
+
97
+	// Ticket privacy
98
+	if (empty($modSettings['shd_privacy_display']) || $modSettings['shd_privacy_display'] == 'smart')
99
+		$context['display_private'] = shd_allowed_to('shd_view_ticket_private_any') || shd_allowed_to('shd_alter_privacy_own') || shd_allowed_to('shd_alter_privacy_any') || $context['ticket_form']['private']['setting'];
100
+	else
101
+		$context['display_private'] = true;
102
+
103
+	if ($new_ticket)
104
+	{
105
+		$context['ticket_form'] += array(
106
+			'member' => array(
107
+				'name' => $context['user']['name'],
108
+				'id' => $context['user']['id'],
109
+				'link' => shd_profile_link($context['user']['name'], $context['user']['id']),
110
+			),
111
+			'assigned' => array(
112
+				'id' => 0,
113
+				'name' => $txt['shd_unassigned'],
114
+				'link' => '<span class="error">' . $txt['shd_unassigned'] . '</span>',
115
+			),
116
+		);
117
+	}
118
+	else
119
+	{
120
+		$context['ticket_form'] += array(
121
+			'member' => array(
122
+				'name' => $ticketinfo['starter_name'],
123
+				'id' => $ticketinfo['starter_id'],
124
+				'link' => shd_profile_link($ticketinfo['starter_name'], $ticketinfo['starter_id']),
125
+			),
126
+			'assigned' => array(
127
+				'id' => $ticketinfo['assigned_id'],
128
+				'name' => !empty($ticketinfo['assigned_id']) ? $ticketinfo['assigned_name'] : $txt['shd_unassigned'],
129
+				'link' => !empty($ticketinfo['assigned_id']) ? shd_profile_link($ticketinfo['assigned_name'], $ticketinfo['assigned_id']) : '<span class="error">' . $txt['shd_unassigned'] . '</span>',
130
+			),
131
+		);
132
+
133
+		loadMemberData($ticketinfo['starter_id']);
134
+		if (loadMemberContext($ticketinfo['starter_id']))
135
+			$context['ticket_form']['member']['avatar'] = $memberContext[$ticketinfo['starter_id']]['avatar'];
136
+
137
+		if (!$new_ticket && !empty($ticketinfo['modified_time']))
138
+		{
139
+			$context['ticket_form'] += array(
140
+				'modified' => array(
141
+					'name' => $ticketinfo['modified_name'],
142
+					'id' => $ticketinfo['modified_id'],
143
+					'time' => timeformat($ticketinfo['modified_time']),
144
+					'link' => shd_profile_link($ticketinfo['modified_name'], $ticketinfo['modified_id']),
145
+				),
146
+			);
147
+		}
148
+
149
+		// Some other setup stuff
150
+		shd_load_attachments();
151
+
152
+		$context['ticket_form']['message'] = un_preparsecode($context['ticket_form']['message']);
153
+		censorText($context['ticket_form']['subject']);
154
+		censorText($context['ticket_form']['message']);
155
+	}
156
+
157
+	shd_get_urgency_options($new_ticket || $ticketinfo['is_own']);
158
+
159
+	if ($context['ticket_form']['num_replies'])
160
+		shd_setup_replies($ticketinfo['id_first_msg']);
161
+
162
+	shd_check_attachments();
163
+
164
+	// Set up the fancy editor
165
+	shd_postbox(
166
+		'shd_message',
167
+		$context['ticket_form']['message'],
168
+		array(
169
+			'post_button' => $context['ticket_form']['form_title'],
170
+		)
171
+	);
172
+
173
+	// Build the link tree and navigation
174
+	$context['linktree'][] = array(
175
+		'url' => $scripturl . '?action=helpdesk;sa=main',
176
+		'name' => $txt['shd_linktree_tickets'],
177
+	);
178
+	$context['linktree'][] = array(
179
+		'name' => $new_ticket ? $txt['shd_create_ticket'] : sprintf($txt['shd_edit_ticket_linktree'], $context['ticket_form']['link']),
180
+	);
181
+
182
+	$context['page_title'] = $txt['shd_helpdesk'];
183
+	$context['sub_template'] = 'ticket_post';
184
+
185
+	// Register this form in the session variables.
186
+	checkSubmitOnce('register');
187
+}
188
+
189
+// All the magically common posting stuff goes in here
190
+function shd_save_post()
191
+{
192
+	global $txt, $modSettings, $sourcedir, $context, $scripturl;
193
+	global $user_info, $options, $smcFunc;
194
+
195
+	// Oh no, robots!
196
+	$context['robot_no_index'] = true;
197
+
198
+	$context['shd_errors'] = array();
199
+
200
+	// We'll probably be needing these.
201
+	require_once($sourcedir . '/Subs-Editor.php');
202
+	require_once($sourcedir . '/Subs-Post.php');
203
+	require_once($sourcedir . '/Subs-SimpleDeskPost.php');
204
+	loadTemplate('SimpleDesk-Post');
205
+	loadLanguage('Errors'); // for some of the errors we already have
206
+	loadLanguage('Post'); // for some of the common post errors
207
+
208
+	if (!empty($_REQUEST['shd_message_mode']) && isset($_REQUEST['shd_message']))
209
+	{
210
+		// If we came from WYSIWYG, we need to convert from HTML to bbc, then unhtml it; then push to $_POST for everything else
211
+		$_REQUEST['shd_message'] = un_htmlspecialchars(html_to_bbc($_REQUEST['shd_message']));
212
+		$_POST['shd_message'] = $_REQUEST['shd_message'];
213
+	}
214
+
215
+	// Check session and double-posting
216
+	checkSubmitOnce('check');
217
+	if (checkSession('post', '', false) != '')
218
+		$context['shd_errors'][] = 'session_timeout';
219
+
220
+	// Clean up the details
221
+	if (!isset($_POST['shd_message']) || $smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['shd_message']), ENT_QUOTES) === '')
222
+	{
223
+		$context['shd_errors'][] = 'no_message';
224
+		$_POST['shd_message'] = '';
225
+	}
226
+	else
227
+	{
228
+		// Pfft.
229
+		if (isset($_POST['shd_message']) && strtolower($_POST['shd_message']) == 'this is simpledesk!')
230
+			fatal_error('You are not King Leonidas...', false);
231
+		$_POST['shd_message'] = $smcFunc['htmlspecialchars']($_POST['shd_message'], ENT_QUOTES);
232
+		preparsecode($_POST['shd_message']);
233
+
234
+		// Make sure there's something underneath all the tags
235
+		if ($smcFunc['htmltrim'](strip_tags(shd_format_text($_POST['shd_message'], false), '<img>')) === '' && (!shd_allowed_to('admin_forum') || strpos($_POST['shd_message'], '[html]') === false))
236
+			$context['shd_errors'][] = 'no_message';
237
+		elseif (!empty($modSettings['max_messageLength']) && $smcFunc['strlen']($_POST['shd_message']) > $modSettings['max_messageLength'])
238
+		{
239
+			$context['shd_errors'][] = 'long_message';
240
+			$txt['error_long_message'] = sprintf($txt['error_long_message'], $modSettings['max_messageLength']);
241
+		}
242
+	}
243
+
244
+	// Now send them off to the specific areas, whether that's saving a ticket or a reply
245
+
246
+	$actions = array(
247
+		'saveticket' => 'shd_save_ticket',
248
+		'savereply' => 'shd_save_reply',
249
+	);
250
+
251
+	if (isset($actions[$_REQUEST['sa']]))
252
+		$actions[$_REQUEST['sa']]();
253
+}
254
+
255
+function shd_save_ticket()
256
+{
257
+	global $txt, $modSettings, $sourcedir, $context, $scripturl;
258
+	global $user_info, $options, $smcFunc, $memberContext;
259
+
260
+	// Ticket's gotta have a subject
261
+	if (!isset($_POST['subject']) || $smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['subject'])) === '')
262
+	{
263
+		$context['shd_errors'][] = 'no_subject';
264
+		$_POST['subject'] = '';
265
+	}
266
+	else
267
+		$_POST['subject'] = strtr($smcFunc['htmlspecialchars']($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
268
+
269
+	if (empty($context['ticket_id']))
270
+	{
271
+		shd_is_allowed_to('shd_new_ticket');
272
+
273
+		// some healthy defaults
274
+		$context['ticket_id'] = 0;
275
+		$new_ticket = true;
276
+		$msg = 0;
277
+		$is_own = true;
278
+		$new_status = TICKET_STATUS_NEW;
279
+		$private = false;
280
+		$urgency = TICKET_URGENCY_LOW;
281
+		$assigned = array(
282
+			'id' => 0,
283
+			'name' => $txt['shd_unassigned'],
284
+			'link' => '<span class="error">' . $txt['shd_unassigned'] . '</span>',
285
+		);
286
+		$num_replies = 0;
287
+	}
288
+	else
289
+	{
290
+		// hmm, we're saving an update, let's get the existing ticket details and we can check permissions and stuff
291
+		$new_ticket = false;
292
+
293
+		$ticketinfo = shd_load_ticket();
294
+
295
+		// S'pose we'd better check the permissions here
296
+		if (!shd_allowed_to('shd_edit_ticket_any') && (!shd_allowed_to('shd_edit_ticket_own') || !$ticketinfo['is_own']))
297
+			fatal_lang_error('cannot_shd_edit_ticket', false);
298
+
299
+		$msg = $ticketinfo['id_first_msg'];
300
+		$is_own = $ticketinfo['is_own'];
301
+		$private = $ticketinfo['private'];
302
+		$urgency = $ticketinfo['urgency'];
303
+		$new_status = $ticketinfo['status'];
304
+		$assigned = array(
305
+			'id' => $ticketinfo['assigned_id'],
306
+			'name' => !empty($ticketinfo['assigned_id']) ? $ticketinfo['assigned_name'] : $txt['shd_unassigned'],
307
+			'link' => !empty($ticketinfo['assigned_id']) ? shd_profile_link($ticketinfo['assigned_name'], $ticketinfo['assigned_id']) : '<span class="error">' . $txt['shd_unassigned'] . '</span>',
308
+		);
309
+		$num_replies = $ticketinfo['num_replies'];
310
+	}
311
+
312
+	$context['ticket_form'] = array(
313
+		'form_title' => $new_ticket ? $txt['shd_create_ticket'] : $txt['shd_edit_ticket'],
314
+		'form_action' => $scripturl . '?action=helpdesk;sa=saveticket',
315
+		'first_msg' => $new_ticket ? 0 : $ticketinfo['id_first_msg'],
316
+		'message' => $_POST['shd_message'],
317
+		'subject' => $_POST['subject'],
318
+		'ticket' => $context['ticket_id'],
319
+		'link' => $new_ticket ? '' : '<a href="' . $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $context['ticket_id'] . '">' . $ticketinfo['subject'] . '</a>',
320
+		'msg' => $msg,
321
+		'display_id' => empty($context['ticket_id']) ? '' : str_pad($context['ticket_id'], 5, '0', STR_PAD_LEFT),
322
+		'status' => $new_status,
323
+		'private' => array(
324
+			'setting' => $private,
325
+			'can_change' => (shd_allowed_to('shd_alter_privacy_any') || ($is_own && shd_allowed_to('shd_alter_privacy_own'))),
326
+			'options' => array(
327
+				0 => 'shd_ticket_notprivate',
328
+				1 => 'shd_ticket_private',
329
+			),
330
+		),
331
+		'assigned' => $assigned,
332
+		'num_replies' => $num_replies,
333
+		'do_attach' => shd_allowed_to('shd_post_attachment'),
334
+		'return_to_ticket' => isset($_REQUEST['goback']),
335
+		'disable_smileys' => !empty($_REQUEST['no_smileys']),
336
+	);
337
+
338
+	// Ticket privacy
339
+	if (empty($modSettings['shd_privacy_display']) || $modSettings['shd_privacy_display'] == 'smart')
340
+		$context['display_private'] = shd_allowed_to('shd_view_ticket_private_any') || shd_allowed_to('shd_alter_privacy_own') || shd_allowed_to('shd_alter_privacy_any') || $context['ticket_form']['private']['setting'];
341
+	else
342
+		$context['display_private'] = true;	
343
+
344
+	// Preview?
345
+	if (isset($_REQUEST['preview']))
346
+	{
347
+		$context['ticket_form']['preview'] = array(
348
+			'title' => $txt['shd_previewing_ticket'] . ': ' . (empty($_POST['subject']) ? '<em>' . $txt['no_subject'] . '</em>' : $_POST['subject']),
349
+			'body' => shd_format_text($_POST['shd_message']),
350
+		);
351
+	}
352
+
353
+	if (!$new_ticket && !empty($ticketinfo['modified_time']))
354
+	{
355
+		$context['ticket_form'] += array(
356
+			'modified' => array(
357
+				'name' => $ticketinfo['modified_name'],
358
+				'id' => $ticketinfo['modified_id'],
359
+				'time' => timeformat($ticketinfo['modified_time']),
360
+				'link' => shd_profile_link($ticketinfo['modified_name'], $ticketinfo['modified_id']),
361
+			),
362
+		);
363
+	}
364
+
365
+	if (!$new_ticket)
366
+	{
367
+		loadMemberData($ticketinfo['starter_id']);
368
+		if (loadMemberContext($ticketinfo['starter_id']))
369
+			$context['ticket_form']['member']['avatar'] = $memberContext[$ticketinfo['starter_id']]['avatar'];
370
+	}
371
+
372
+	shd_load_attachments();
373
+
374
+	// Ticket privacy, let's see if we can override our healthy default with the post value
375
+	if ($context['ticket_form']['private']['can_change'])
376
+	{
377
+		$new_private = isset($_POST['shd_private']) ? (int) $_POST['shd_private'] : $private;
378
+		$context['ticket_form']['private']['setting'] = isset($context['ticket_form']['private']['options'][$new_private]) ? (bool) $new_private : $private;
379
+	}
380
+
381
+	// Ticket urgency
382
+	shd_get_urgency_options($is_own);
383
+	if ($context['ticket_form']['urgency']['can_change'])
384
+	{
385
+		$new_urgency = isset($_POST['shd_urgency']) ? (int) $_POST['shd_urgency'] : $urgency;
386
+		$context['ticket_form']['urgency']['setting'] = isset($context['ticket_form']['urgency']['options'][$new_urgency]) ? $new_urgency : $urgency;
387
+	}
388
+	else
389
+		$context['ticket_form']['urgency']['setting'] = $urgency;
390
+
391
+	// A few basic checks
392
+	if ($context['ticket_form']['status'] == TICKET_STATUS_CLOSED)
393
+		fatal_lang_error('shd_cannot_edit_closed', false);
394
+	elseif ($context['ticket_form']['status'] == TICKET_STATUS_DELETED)
395
+		fatal_lang_error('shd_cannon_edit_deleted', false);
396
+
397
+	if (!empty($context['shd_errors']) || !empty($context['ticket_form']['preview'])) // Uh oh, something went wrong, or we're previewing
398
+	{
399
+		checkSubmitOnce('free');
400
+
401
+		// Anything else for redisplaying the form
402
+		$context['page_title'] = $txt['shd_helpdesk'];
403
+		$context['sub_template'] = 'ticket_post';
404
+
405
+		shd_check_attachments();
406
+
407
+		// Set up the fancy editor
408
+		shd_postbox(
409
+			'shd_message',
410
+			un_preparsecode($context['ticket_form']['message']),
411
+			array(
412
+				'post_button' => $context['ticket_form']['form_title'],
413
+			)
414
+		);
415
+
416
+		// Build the link tree and navigation
417
+		$context['linktree'][] = array(
418
+			'url' => $scripturl . '?action=helpdesk;sa=main',
419
+			'name' => $txt['shd_linktree_tickets'],
420
+		);
421
+		$context['linktree'][] = array(
422
+			'name' => $new_ticket ? $txt['shd_create_ticket'] : sprintf($txt['shd_edit_ticket_linktree'], $context['ticket_form']['link']),
423
+		);
424
+
425
+		checkSubmitOnce('register');
426
+	}
427
+	else
428
+	{
429
+		// It all worked, w00t, so let's get ready to rumble
430
+		$attachIDs = shd_handle_attachments();
431
+
432
+		if ($new_ticket)
433
+		{
434
+			// Now to add the ticket details
435
+			$posterOptions = array(
436
+				'id' => $user_info['id'],
437
+			);
438
+
439
+			$msgOptions = array(
440
+				'body' => $context['ticket_form']['message'],
441
+				'id' => $context['ticket_form']['msg'],
442
+				'smileys_enabled' => empty($context['ticket_form']['disable_smileys']),
443
+				'attachments' => $attachIDs,
444
+			);
445
+
446
+			$ticketOptions = array(
447
+				'id' => $context['ticket_form']['ticket'],
448
+				'mark_as_read' => true,
449
+				'subject' => $context['ticket_form']['subject'],
450
+				'private' => $context['ticket_form']['private']['setting'],
451
+				'status' => $context['ticket_form']['status'],
452
+				'urgency' => $context['ticket_form']['urgency']['setting'],
453
+				'assigned' => $context['ticket_form']['assigned']['id'],
454
+			);
455
+			shd_create_ticket_post($msgOptions, $ticketOptions, $posterOptions);
456
+			shd_clear_active_tickets();
457
+
458
+			// Update our nice ticket store with the ticket id
459
+			$context['ticket_id'] = $ticketOptions['id'];
460
+			$context['ticket_form']['ticket'] = $ticketOptions['id'];
461
+		}
462
+		else
463
+		{
464
+			// Only add what has actually changed
465
+			// Now to add the ticket details
466
+			$posterOptions = array();
467
+
468
+			$msgOptions = array(
469
+				'id' => $context['ticket_form']['msg'],
470
+				'attachments' => $attachIDs,
471
+			);
472
+
473
+			$ticketOptions = array(
474
+				'id' => $context['ticket_form']['ticket'],
475
+			);
476
+
477
+			if ((bool) $ticketinfo['smileys_enabled'] == $context['ticket_form']['disable_smileys']) // since one is enabled, one is 'now disable'...
478
+				$msgOptions['smileys_enabled'] = !$context['ticket_form']['disable_smileys'];
479
+
480
+			// This things don't trigger modified time
481
+			if ($ticketinfo['private'] != $context['ticket_form']['private']['setting'])
482
+			{
483
+				$ticketOptions['private'] = $context['ticket_form']['private']['setting'];
484
+				// log the change too
485
+				$action = empty($context['ticket_form']['private']['setting']) ? 'marknotprivate' : 'markprivate'; // i.e. based on new setting
486
+				shd_log_action($action,
487
+					array(
488
+						'ticket' => $context['ticket_form']['ticket'],
489
+						'subject' => $context['ticket_form']['subject'],
490
+					)
491
+				);
492
+			}
493
+			if ($ticketinfo['urgency'] != $context['ticket_form']['urgency']['setting'])
494
+			{
495
+				$ticketOptions['urgency'] = $context['ticket_form']['urgency']['setting'];
496
+				// log the change too
497
+				$action = ($context['ticket_form']['urgency']['setting'] > $ticketinfo['urgency']) ? 'urgency_increase' : 'urgency_decrease';
498
+				shd_log_action($action,
499
+					array(
500
+						'ticket' => $context['ticket_form']['ticket'],
501
+						'subject' => $context['ticket_form']['subject'],
502
+						'urgency' => $context['ticket_form']['urgency']['setting'],
503
+					)
504
+				);
505
+			}
506
+
507
+			// But these things do!
508
+			if ($ticketinfo['subject'] != $context['ticket_form']['subject'])
509
+				$ticketOptions['subject'] = $context['ticket_form']['subject'];
510
+			if ($ticketinfo['body'] != $context['ticket_form']['message'])
511
+				$msgOptions['body'] = $context['ticket_form']['message'];
512
+
513
+			if (isset($ticketOptions['subject']) || isset($msgOptions['body']))
514
+				$msgOptions['modified'] = array(
515
+					'id' => $user_info['id'],
516
+					'name' => $user_info['name'],
517
+					'time' => time(),
518
+				);
519
+
520
+			// DOOOOOOOO EEEEEEEEEEET NAO!
521
+			shd_modify_ticket_post($msgOptions, $ticketOptions, $posterOptions);
522
+			shd_clear_active_tickets($ticketinfo['id_member_started']);
523
+		}
524
+		shd_done_posting();
525
+	}
526
+}
527
+
528
+function shd_post_reply()
529
+{
530
+	global $context, $user_info, $sourcedir, $txt, $scripturl, $smcFunc, $reply_request, $modSettings, $settings, $options, $memberContext;
531
+
532
+	checkSession('get');
533
+
534
+	$new_reply = $_REQUEST['sa'] == 'reply';
535
+
536
+	// Things we need
537
+	loadTemplate('SimpleDesk-Post');
538
+	require_once($sourcedir . '/Subs-Post.php');
539
+	require_once($sourcedir . '/Subs-SimpleDeskPost.php');
540
+	require_once($sourcedir . '/Subs-Editor.php');
541
+
542
+	$ticketinfo = shd_load_ticket();
543
+	$reply = array();
544
+
545
+	// So, at this point, we can see it, but no guarantee we can reply to it.
546
+	// Can we reply to any? If so, just go right along. If not, we need to do more work.
547
+	if ($new_reply)
548
+	{
549
+		if (!shd_allowed_to('shd_reply_ticket_any'))
550
+		{
551
+			if (shd_allowed_to('shd_reply_ticket_own'))
552
+			{
553
+				if (!$ticketinfo['is_own'])
554
+					fatal_lang_error('shd_cannot_reply_any_but_own', false);
555
+			}
556
+			else
557
+				fatal_lang_error('shd_cannot_reply_any', false); // can't do nuthin'
558
+		}
559
+	}
560
+	else
561
+	{
562
+		$_REQUEST['msg'] = !empty($_REQUEST['msg']) ? (int) $_REQUEST['msg'] : 0;
563
+		$query = shd_db_query('', '
564
+			SELECT id_msg, id_member, body, modified_time, modified_member, modified_name
565
+			FROM {db_prefix}helpdesk_ticket_replies
566
+			WHERE id_msg = {int:msg}
567
+				AND id_ticket = {int:ticket}',
568
+			array(
569
+				'msg' => $_REQUEST['msg'],
570
+				'ticket' => $context['ticket_id'],
571
+			)
572
+		);
573
+
574
+		if ($smcFunc['db_num_rows']($query) == 0)
575
+		{
576
+			$smcFunc['db_free_result']($query);
577
+			fatal_lang_error('shd_no_ticket', false);
578
+		}
579
+
580
+		$reply = $smcFunc['db_fetch_assoc']($query);
581
+
582
+		if (!shd_allowed_to('shd_edit_reply_any'))
583
+		{
584
+			if (shd_allowed_to('shd_edit_reply_own'))
585
+			{
586
+				if ($reply['id_member'] != $user_info['id'])
587
+					fatal_lang_error('shd_cannot_edit_reply_any_but_own', false);
588
+			}
589
+			else
590
+				fatal_lang_error('shd_cannot_edit_reply_any', false);
591
+		}
592
+
593
+		// Fix the body up for later
594
+		$reply['body'] = un_preparsecode($reply['body']);
595
+		censorText($reply['body']);
596
+	}
597
+
598
+	// So it's either our ticket and we can reply to our own, or we can reply to any because we're awesome
599
+	// or we're editing and we can haz such powarz
600
+	$context['ticket_form'] = array( // yes, everything goes in here.
601
+		'form_title' => !empty($reply['id_msg']) ? $txt['shd_ticket_edit_reply'] : $txt['shd_reply_ticket'],
602
+		'form_action' => $scripturl . '?action=helpdesk;sa=savereply',
603
+		'first_msg' => $new_reply ? 0 : $ticketinfo['id_first_msg'],
604
+		'message' => shd_format_text($ticketinfo['body'], $ticketinfo['smileys_enabled'], 'shd_ticket_' . $context['ticket_id']),
605
+		'subject' => $ticketinfo['subject'],
606
+		'ticket' => $context['ticket_id'],
607
+		'msg' => !empty($reply['id_msg']) ? $reply['id_msg'] : 0,
608
+		'ticket_link' => '<a href="' . $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $context['ticket_id'] . '">' . $ticketinfo['subject'] . '</a>',
609
+		'reply_link' => $new_reply ? 0 : '<a href="' . $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $context['ticket_id'] . '.msg' . $reply['id_msg'] . '#msg' . $reply['id_msg'] . '">' . $txt['response_prefix'] . ' ' . $ticketinfo['subject'] . '</a>',
610
+		'display_id' => str_pad($context['ticket_id'], 5, '0', STR_PAD_LEFT),
611
+		'status' => $ticketinfo['status'],
612
+		'urgency' => array(
613
+			'setting' => $ticketinfo['urgency'],
614
+		),
615
+		'private' => array(
616
+			'setting' => $ticketinfo['private'],
617
+			'can_change' => false,
618
+			'options' => array(
619
+				0 => 'shd_ticket_notprivate',
620
+				1 => 'shd_ticket_private',
621
+			),
622
+		),
623
+		'errors' => array(),
624
+		'member' => array(
625
+			'name' => $ticketinfo['starter_name'],
626
+			'id' => $ticketinfo['starter_id'],
627
+			'link' => shd_profile_link($ticketinfo['starter_name'], $ticketinfo['starter_id']),
628
+		),
629
+		'assigned' => array(
630
+			'id' => $ticketinfo['assigned_id'],
631
+			'name' => !empty($ticketinfo['assigned_id']) ? $ticketinfo['assigned_name'] : $txt['shd_unassigned'],
632
+			'link' => !empty($ticketinfo['assigned_id']) ? shd_profile_link($ticketinfo['assigned_name'], $ticketinfo['assigned_id']) : '<span class="error">' . $txt['shd_unassigned'] . '</span>',
633
+		),
634
+		'num_replies' => !empty($ticketinfo['num_replies']) ? $ticketinfo['num_replies'] : 0,
635
+		'do_attach' => shd_allowed_to('shd_post_attachment'),
636
+		'reply' => !empty($reply['body']) ? $reply['body'] : '',
637
+		'return_to_ticket' => isset($_REQUEST['goback']),
638
+		'disable_smileys' => !$new_reply ? !empty($_REQUEST['no_smileys']) : ($ticketinfo['smileys_enabled'] == 0),
639
+	);
640
+
641
+	// Ticket privacy
642
+	if (empty($modSettings['shd_privacy_display']) || $modSettings['shd_privacy_display'] == 'smart')
643
+		$context['display_private'] = shd_allowed_to('shd_view_ticket_private_any') || shd_allowed_to('shd_alter_privacy_own') || shd_allowed_to('shd_alter_privacy_any') || $context['ticket_form']['private']['setting'];
644
+	else
645
+		$context['display_private'] = true;
646
+
647
+	loadMemberData($ticketinfo['starter_id']);
648
+	if (loadMemberContext($ticketinfo['starter_id']))
649
+		$context['ticket_form']['member']['avatar'] = $memberContext[$ticketinfo['starter_id']]['avatar'];
650
+
651
+	if (!empty($ticketinfo['modified_time']))
652
+	{
653
+		$context['ticket_form'] += array(
654
+			'modified' => array(
655
+				'name' => $ticketinfo['modified_name'],
656
+				'id' => $ticketinfo['modified_id'],
657
+				'time' => timeformat($ticketinfo['modified_time']),
658
+				'link' => shd_profile_link($ticketinfo['modified_name'], $ticketinfo['modified_id']),
659
+			),
660
+		);
661
+	}
662
+
663
+	shd_get_urgency_options($ticketinfo['is_own']);
664
+	$context['ticket_form']['urgency']['can_change'] = false;
665
+
666
+	if (!empty($ticketinfo['num_replies']))
667
+		shd_setup_replies($ticketinfo['id_first_msg']);
668
+
669
+	// A few basic checks
670
+	if ($context['ticket_form']['status'] == TICKET_STATUS_CLOSED)
671
+		fatal_lang_error('shd_cannot_reply_closed', false);
672
+	elseif ($context['ticket_form']['status'] == TICKET_STATUS_DELETED)
673
+		fatal_lang_error('shd_cannon_reply_deleted', false);
674
+
675
+	shd_load_attachments();
676
+	shd_check_attachments();
677
+
678
+	if (empty($options['no_new_reply_warning']) && isset($_REQUEST['num_replies']))
679
+	{
680
+		$_REQUEST['num_replies'] = (int) $_REQUEST['num_replies'];
681
+		$newReplies = $context['ticket_form']['num_replies'] > $_REQUEST['num_replies'] ? $context['ticket_form']['num_replies'] - $_REQUEST['num_replies'] : 0;
682
+
683
+		if (!empty($newReplies))
684
+		{
685
+			loadLanguage('Post');
686
+			if ($newReplies > 1)
687
+				$txt['error_new_replies_reading'] = sprintf($txt['error_new_replies_reading'], $newReplies);
688
+
689
+			$context['shd_errors'][] = $newReplies == 1 ? 'new_reply_reading' : 'new_replies_reading';
690
+		}
691
+	}
692
+
693
+	// Are we quoting something? Let's see if we do; we already know we can see the ticket
694
+	$_REQUEST['quote'] = !empty($_REQUEST['quote']) ? (int) $_REQUEST['quote'] : 0;
695
+	if (!empty($_REQUEST['quote']))
696
+	{
697
+		$query = shd_db_query('', '
698
+			SELECT hdtr.body, IFNULL(mem.real_name, hdtr.poster_name) AS poster_name, hdtr.poster_time
699
+			FROM {db_prefix}helpdesk_ticket_replies AS hdtr
700
+				LEFT JOIN {db_prefix}members AS mem ON (hdtr.id_member = mem.id_member)
701
+			WHERE id_msg = {int:msg}
702
+				AND id_ticket = {int:ticket}',
703
+			array(
704
+				'msg' => $_REQUEST['quote'],
705
+				'ticket' => $context['ticket_id'], // make sure it's actually in this ticket!
706
+			)
707
+		);
708
+
709
+		if ($row = $smcFunc['db_fetch_assoc']($query))
710
+		{
711
+			$smcFunc['db_free_result']($query);
712
+
713
+			$row['body'] = un_preparsecode($row['body']);
714
+
715
+			// Censor the message!
716
+			censorText($row['body']);
717
+			$row['body'] = preg_replace('~<br ?/?' . '>~i', "\n", $row['body']);
718
+
719
+			if (strpos($row['poster_name'], '[') !== false || strpos($row['poster_name'], ']') !== false)
720
+				$row['poster_name'] = '"' . $row['poster_name'] . '"';
721
+
722
+			// Make the body HTML if need be.
723
+			if (!empty($_REQUEST['mode']))
724
+			{
725
+				require_once($sourcedir . '/Subs-Editor.php');
726
+				$row['body'] = strtr($row['body'], array('&lt;' => '#smlt#', '&gt;' => '#smgt#', '&amp;' => '#smamp#'));
727
+				$row['body'] = bbc_to_html($row['body']);
728
+				$lb = '<br />';
729
+			}
730
+			else
731
+				$lb = "\n";
732
+
733
+			$message = '[quote author=' . $row['poster_name'] . ' link=action=helpdesk;sa=ticket;ticket=' . $context['ticket_id'];
734
+			if ($ticketinfo['id_first_msg'] != $_REQUEST['quote']) // don't add the msg if we're quoting the ticket itself
735
+				$message .= '.msg' . $_REQUEST['quote'] . '#msg' . $_REQUEST['quote'];
736
+
737
+			$message .= ' date=' . $row['poster_time'] . ']' . $lb . $row['body'] . $lb . '[/quote]';
738
+			$context['ticket_form']['reply'] = $message;
739
+		}
740
+	}
741
+
742
+	// Set up the awesomeness that is the rich editor
743
+	shd_postbox(
744
+		'shd_message',
745
+		$context['ticket_form']['reply'],
746
+		array(
747
+			'post_button' => !empty($reply['id_msg']) ? $txt['shd_ticket_edit_reply'] : $txt['shd_reply_ticket'],
748
+		)
749
+	);
750
+
751
+	// Build the link tree and navigation
752
+	$context['linktree'][] = array(
753
+		'url' => $scripturl . '?action=helpdesk;sa=main',
754
+		'name' => $txt['shd_linktree_tickets'],
755
+	);
756
+	$context['linktree'][] = array(
757
+		'name' => $new_reply ? sprintf($txt['shd_reply_ticket_linktree'], $context['ticket_form']['ticket_link']) : sprintf($txt['shd_edit_reply_linktree'], $context['ticket_form']['reply_link']),
758
+	);
759
+
760
+	$context['page_title'] = $txt['shd_helpdesk'];
761
+	$context['sub_template'] = 'reply_post';
762
+
763
+	// Register this form in the session variables.
764
+	checkSubmitOnce('register');
765
+}
766
+
767
+function shd_save_reply()
768
+{
769
+	global $txt, $modSettings, $sourcedir, $context, $scripturl;
770
+	global $user_info, $options, $smcFunc, $memberContext;
771
+
772
+	$_REQUEST['msg'] = !empty($_REQUEST['msg']) ? (int) $_REQUEST['msg'] : 0;
773
+
774
+	// We're replying so there must be an existing ticket or sumthin's WRONG.
775
+	$ticketinfo = shd_load_ticket();
776
+	$reply = array();
777
+	$new_reply = $_REQUEST['msg'] == 0;
778
+
779
+	// So, at this point, we can see it, but no guarantee we can reply to it.
780
+	// Can we reply to any? If so, just go right along. If not, we need to do more work.
781
+	if ($new_reply)
782
+	{
783
+		if (!shd_allowed_to('shd_reply_ticket_any'))
784
+		{
785
+			if (shd_allowed_to('shd_reply_ticket_own'))
786
+			{
787
+				if (!$ticketinfo['is_own'])
788
+					fatal_lang_error('shd_cannot_reply_any_but_own', false);
789
+			}
790
+			else
791
+				fatal_lang_error('shd_cannot_reply_any', false); // can't do nuthin'
792
+		}
793
+	}
794
+	else
795
+	{
796
+		$query = shd_db_query('', '
797
+			SELECT id_msg, id_member, body, modified_time, modified_member, modified_name
798
+			FROM {db_prefix}helpdesk_ticket_replies
799
+			WHERE id_msg = {int:msg}
800
+				AND id_ticket = {int:ticket}',
801
+			array(
802
+				'msg' => $_REQUEST['msg'],
803
+				'ticket' => $context['ticket_id'],
804
+			)
805
+		);
806
+
807
+		if ($smcFunc['db_num_rows']($query) == 0)
808
+		{
809
+			$smcFunc['db_free_result']($query);
810
+			fatal_lang_error('shd_no_ticket', false);
811
+		}
812
+
813
+		$reply = $smcFunc['db_fetch_assoc']($query);
814
+
815
+		if (!shd_allowed_to('shd_edit_reply_any'))
816
+		{
817
+			if (shd_allowed_to('shd_edit_reply_own'))
818
+			{
819
+				if ($reply['id_member'] != $user_info['id'])
820
+					fatal_lang_error('shd_cannot_edit_reply_any_but_own', false);
821
+			}
822
+			else
823
+				fatal_lang_error('shd_cannot_edit_reply_any', false);
824
+		}
825
+	}
826
+
827
+	$context['ticket_form'] = array(
828
+		'form_title' => $new_reply ? $txt['shd_reply_ticket'] : $txt['shd_ticket_edit_reply'],
829
+		'form_action' => $scripturl . '?action=helpdesk;sa=savereply',
830
+		'first_msg' => $new_reply ? 0 : $ticketinfo['id_first_msg'],
831
+		'message' => shd_format_text($ticketinfo['body'], $ticketinfo['smileys_enabled'], 'shd_ticket_' . $context['ticket_id']),
832
+		'subject' => $ticketinfo['subject'],
833
+		'ticket' => $context['ticket_id'],
834
+		'msg' => $_REQUEST['msg'],
835
+		'display_id' => str_pad($context['ticket_id'], 5, '0', STR_PAD_LEFT),
836
+		'urgency' => array(
837
+			'setting' => $ticketinfo['urgency'],
838
+		),
839
+		'private' => array(
840
+			'setting' => $ticketinfo['private'],
841
+			'can_change' => false,
842
+			'options' => array(
843
+				0 => 'shd_ticket_notprivate',
844
+				1 => 'shd_ticket_private',
845
+			),
846
+		),
847
+		'status' => $ticketinfo['status'], // reuse existing status for now
848
+		'assigned' => array(
849
+			'id' => $ticketinfo['assigned_id'],
850
+			'name' => !empty($ticketinfo['assigned_id']) ? $ticketinfo['assigned_name'] : $txt['shd_unassigned'],
851
+			'link' => !empty($ticketinfo['assigned_id']) ? shd_profile_link($ticketinfo['assigned_name'], $ticketinfo['assigned_id']) : '<span class="error">' . $txt['shd_unassigned'] . '</span>',
852
+		),
853
+		'num_replies' => $ticketinfo['num_replies'],
854
+		'do_attach' => shd_allowed_to('shd_post_attachment'),
855
+		'reply' => $_POST['shd_message'],
856
+		'return_to_ticket' => isset($_REQUEST['goback']),
857
+		'disable_smileys' => !empty($_REQUEST['no_smileys']),
858
+	);
859
+
860
+	// Ticket privacy
861
+	if (empty($modSettings['shd_privacy_display']) || $modSettings['shd_privacy_display'] == 'smart')
862
+		$context['display_private'] = shd_allowed_to('shd_view_ticket_private_any') || shd_allowed_to('shd_alter_privacy_own') || shd_allowed_to('shd_alter_privacy_any') || $context['ticket_form']['private']['setting'];
863
+	else
864
+		$context['display_private'] = true;
865
+
866
+	loadMemberData($ticketinfo['starter_id']);
867
+	if (loadMemberContext($ticketinfo['starter_id']))
868
+		$context['ticket_form']['member']['avatar'] = $memberContext[$ticketinfo['starter_id']]['avatar'];
869
+
870
+	if (!empty($ticketinfo['modified_time']))
871
+	{
872
+		$context['ticket_form'] += array(
873
+			'modified' => array(
874
+				'name' => $ticketinfo['modified_name'],
875
+				'id' => $ticketinfo['modified_id'],
876
+				'time' => timeformat($ticketinfo['modified_time']),
877
+				'link' => shd_profile_link($ticketinfo['modified_name'], $ticketinfo['modified_id']),
878
+			),
879
+		);
880
+	}
881
+
882
+	if (isset($_REQUEST['preview']))
883
+	{
884
+		$context['ticket_form']['preview'] = array(
885
+			'title' => $txt['shd_previewing_reply'] . ': ' . (empty($context['ticket_form']['subject']) ? '<em>' . $txt['no_subject'] . '</em>' : $context['ticket_form']['subject']),
886
+			'body' => shd_format_text($_POST['shd_message']),
887
+		);
888
+	}
889
+
890
+	shd_load_attachments();
891
+
892
+	shd_get_urgency_options($ticketinfo['is_own']);
893
+	$context['ticket_form']['urgency']['can_change'] = false;
894
+
895
+	if (!empty($ticketinfo['num_replies']))
896
+		shd_setup_replies($ticketinfo['id_first_msg']);
897
+
898
+	// A few basic checks
899
+	if ($context['ticket_form']['status'] == TICKET_STATUS_CLOSED)
900
+		fatal_lang_error('shd_cannot_edit_closed', false);
901
+	elseif ($context['ticket_form']['status'] == TICKET_STATUS_DELETED)
902
+		fatal_lang_error('shd_cannon_edit_deleted', false);
903
+
904
+	// Have there been any new replies that we missed?
905
+	if (empty($options['no_new_reply_warning']) && isset($_REQUEST['num_replies']))
906
+	{
907
+		$_REQUEST['num_replies'] = (int) $_REQUEST['num_replies'];
908
+		$newReplies = $context['ticket_form']['num_replies'] > $_REQUEST['num_replies'] ? $context['ticket_form']['num_replies'] - $_REQUEST['num_replies'] : 0;
909
+
910
+		if (!empty($newReplies))
911
+		{
912
+			loadLanguage('Post');
913
+			if ($newReplies > 1)
914
+				$txt['error_new_replies'] = sprintf($txt['error_new_replies'], $newReplies);
915
+
916
+			$context['shd_errors'][] = $newReplies == 1 ? 'new_reply' : 'new_replies';
917
+		}
918
+	}
919
+
920
+	if (!empty($context['shd_errors']) || !empty($context['ticket_form']['preview'])) // Uh oh, something went wrong, or we're previewing
921
+	{
922
+		checkSubmitOnce('free');
923
+
924
+		// Anything else for redisplaying the form
925
+		$context['page_title'] = $txt['shd_helpdesk'];
926
+		$context['sub_template'] = 'reply_post';
927
+
928
+		shd_check_attachments();
929
+
930
+		// Set up the fancy editor
931
+		shd_postbox(
932
+			'shd_message',
933
+			un_preparsecode($_POST['shd_message']),
934
+			array(
935
+				'post_button' => $new_reply ? $txt['shd_reply_ticket'] : $txt['shd_ticket_edit_reply'],
936
+			)
937
+		);
938
+
939
+		// Build the link tree and navigation
940
+		$context['linktree'][] = array(
941
+			'url' => $scripturl . '?action=helpdesk;sa=main',
942
+			'name' => $txt['shd_linktree_tickets'],
943
+		);
944
+		$context['linktree'][] = array(
945
+			'name' => $txt['shd_reply_ticket'],
946
+		);
947
+
948
+		checkSubmitOnce('register');
949
+	}
950
+	else
951
+	{
952
+		// It all worked, w00t, so let's get ready to rumble
953
+		$attachIDs = shd_handle_attachments();
954
+		shd_clear_active_tickets($ticketinfo['id_member_started']);
955
+
956
+		if ($new_reply)
957
+		{
958
+			// So... what is the new status?
959
+			$new_status = shd_determine_status('reply', $ticketinfo['starter_id'], $user_info['id']);
960
+
961
+			// Now to add the ticket details
962
+			$posterOptions = array(
963
+				'id' => $user_info['id'],
964
+			);
965
+
966
+			$msgOptions = array(
967
+				'body' => $_POST['shd_message'],
968
+				'id' => $context['ticket_form']['msg'],
969
+				'smileys_enabled' => empty($context['ticket_form']['disable_smileys']),
970
+				'attachments' => $attachIDs,
971
+			);
972
+
973
+			$ticketOptions = array(
974
+				'id' => $context['ticket_form']['ticket'],
975
+				'mark_as_read' => true,
976
+				'status' => $new_status,
977
+			);
978
+			shd_create_ticket_post($msgOptions, $ticketOptions, $posterOptions);
979
+		}
980
+		else
981
+		{
982
+			// Only add what has actually changed
983
+			// Now to add the ticket details
984
+			$posterOptions = array();
985
+
986
+			$msgOptions = array(
987
+				'id' => $context['ticket_form']['msg'],
988
+				'attachments' => $attachIDs,
989
+			);
990
+
991
+			$ticketOptions = array(
992
+				'id' => $context['ticket_form']['ticket'],
993
+			);
994
+
995
+			if ((bool) $ticketinfo['smileys_enabled'] == $context['ticket_form']['disable_smileys']) // since one is enabled, one is 'now disable'...
996
+				$msgOptions['smileys_enabled'] = !$context['ticket_form']['disable_smileys'];
997
+
998
+			if ($reply['body'] != $context['ticket_form']['reply'])
999
+				$msgOptions['body'] = $context['ticket_form']['reply'];
1000
+
1001
+			if (isset($msgOptions['body']))
1002
+				$msgOptions['modified'] = array(
1003
+					'id' => $user_info['id'],
1004
+					'name' => $user_info['name'],
1005
+					'time' => time(),
1006
+				);
1007
+
1008
+			// DOOOOOOOO EEEEEEEEEEET NAO!
1009
+			shd_modify_ticket_post($msgOptions, $ticketOptions, $posterOptions);
1010
+		}
1011
+
1012
+		shd_done_posting();
1013
+	}
1014
+}
1015
+
1016
+/**
1017
+ *	Redirects the user once a ticket has been posted or replied to.
1018
+ *
1019
+ *	Directs the user based on where they were and what was selected:
1020
+ *	- if 'return to ticket' was not selected, return to the front page
1021
+ *	- if it was, and we edited a reply, return to the reply
1022
+ *	- if it was select, but it was a new topic/reply, return just to the topic itself
1023
+ *
1024
+ *	@since 1.0
1025
+*/
1026
+function shd_done_posting()
1027
+{
1028
+	global $context;
1029
+	if (empty($_REQUEST['goback']))
1030
+		redirectexit('action=helpdesk;sa=main');
1031
+	elseif (!empty($context['ticket_form']['msg']))
1032
+		redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id'] . '.msg' . $context['ticket_form']['msg'] . '#msg' . $context['ticket_form']['msg'], $context['browser']['is_ie']);
1033
+	else
1034
+		redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id']);
1035
+}
1036
+
1037
+function shd_setup_replies($first_msg)
1038
+{
1039
+	global $reply_request, $context, $smcFunc, $sourcedir, $modSettings, $settings;
1040
+
1041
+	$context['ticket_form']['do_replies'] = false;
1042
+	$context['can_quote'] = false;
1043
+
1044
+	// OK, we're done with the ticket's own data. Now for replies.
1045
+	$context['get_replies'] = 'shd_prepare_reply_context';
1046
+	$query = shd_db_query('', '
1047
+		SELECT id_msg, id_member, modified_member
1048
+		FROM {db_prefix}helpdesk_ticket_replies
1049
+		WHERE id_ticket = {int:ticket}
1050
+			AND message_status = {int:msg_normal}
1051
+			AND id_msg > {int:first_msg}' . ((empty($context['ticket_form']['msg']) || $context['ticket_form']['msg'] == $context['ticket_form']['first_msg']) ? '' : '
1052
+			AND id_msg < {int:reply_msg}') . '
1053
+		ORDER BY id_msg DESC
1054
+		LIMIT 10',
1055
+		array(
1056
+			'ticket' => $context['ticket_id'],
1057
+			'msg_normal' => MSG_STATUS_NORMAL,
1058
+			'first_msg' => $first_msg,
1059
+			'reply_msg' => !empty($context['ticket_form']['msg']) ? $context['ticket_form']['msg'] : 0,
1060
+		)
1061
+	);
1062
+
1063
+	$messages = array();
1064
+	$posters = array();
1065
+	while ($row = $smcFunc['db_fetch_assoc']($query))
1066
+	{
1067
+		if (!empty($row['id_member']))
1068
+			$posters[] = $row['id_member'];
1069
+
1070
+		if (!empty($row['modified_member']))
1071
+			$posters[] = $row['modified_member'];
1072
+
1073
+		$messages[] = $row['id_msg'];
1074
+	}
1075
+	$smcFunc['db_free_result']($query);
1076
+
1077
+	$posters = array_unique($posters);
1078
+
1079
+	$context['is_team'] = array();
1080
+
1081
+	if (!empty($messages))
1082
+	{
1083
+		$context['ticket_form']['do_replies'] = true;
1084
+
1085
+		// Get the poster data
1086
+		if (!empty($posters))
1087
+		{
1088
+			loadMemberData($posters);
1089
+
1090
+			// Are they current team members?
1091
+			$team = array_intersect($posters, shd_members_allowed_to('shd_staff'));
1092
+
1093
+			foreach ($team as $member)
1094
+				$context['is_team'][$member] = true;
1095
+		}
1096
+
1097
+		$reply_request = shd_db_query('', '
1098
+			SELECT
1099
+				id_msg, poster_time, poster_ip, id_member, modified_time, modified_name, modified_member, body,
1100
+				smileys_enabled, poster_name, poster_email
1101
+			FROM {db_prefix}helpdesk_ticket_replies
1102
+			WHERE id_msg IN ({array_int:message_list})
1103
+			ORDER BY id_msg DESC',
1104
+			array(
1105
+				'message_list' => $messages,
1106
+			)
1107
+		);
1108
+
1109
+		// Can we have a quote button in the replies? If so, we also need the relevant JS instantiation
1110
+		if (!empty($modSettings['shd_allow_ticket_bbc']))
1111
+		{
1112
+			$context['can_quote'] = true;
1113
+			$context['html_headers'] .= '
1114
+			<script type="text/javascript"><!-- // --><![CDATA[
1115
+			var oQuickReply = new QuickReply({
1116
+				bDefaultCollapsed: false,
1117
+				iTicketId: ' . $context['ticket_id'] . ',
1118
+				iStart: ' . $context['start'] . ',
1119
+				sScriptUrl: smf_scripturl,
1120
+				sImagesUrl: "' . $settings['images_url'] . '",
1121
+				sContainerId: "quickReplyOptions",
1122
+				sImageId: "quickReplyExpand",
1123
+				sImageCollapsed: "collapse.gif",
1124
+				sImageExpanded: "expand.gif",
1125
+				sJumpAnchor: "quickreply",
1126
+				sHeaderId: "quickreplyheader",
1127
+				sFooterId: "quickreplyfooter"
1128
+			});
1129
+			// ]' . ']></script>';
1130
+		}
1131
+	}
1132
+	else
1133
+	{
1134
+		$reply_request = false;
1135
+		$context['first_message'] = 0;
1136
+		$context['first_new_message'] = false;
1137
+	}
1138
+}
1139
+
1140
+function shd_postbox($id, $message, $buttons, $width = '70%')
1141
+{
1142
+	global $context, $txt, $modSettings;
1143
+	$editorOptions = array(
1144
+		'id' => $id,
1145
+		'value' => $message,
1146
+		'labels' => $buttons,
1147
+		'preview_type' => 0,
1148
+		'width' => $width,
1149
+		'disable_smiley_box' => empty($modSettings['shd_allow_ticket_bbc']) || $context['ticket_form']['disable_smileys'],
1150
+	);
1151
+	if (empty($modSettings['shd_allow_ticket_bbc']))
1152
+		$modSettings['disable_wysiwyg'] = 1;
1153
+
1154
+	// Hide any disabled tags.
1155
+	if (!empty($modSettings['shd_enabled_bbc']))
1156
+	{
1157
+		$enabled_tags = explode(',', $modSettings['shd_enabled_bbc']);
1158
+		$disabled_tags = array();
1159
+
1160
+		// Loop through all the tags there are in this forum, and and each disabled tag to our 'hide-list'.
1161
+		foreach (parse_bbc(false) AS $tag)
1162
+		{
1163
+			if (!in_array($tag['tag'], $enabled_tags) || $modSettings['shd_enabled_bbc'] == 'shd_all_tags_disabled')
1164
+				$disabled_tags[] = $tag['tag'];
1165
+		}
1166
+
1167
+		// Add them to the editors hide var.
1168
+		$modSettings['disabledBBC'] = implode(',', $disabled_tags);
1169
+	}
1170
+
1171
+	create_control_richedit($editorOptions);
1172
+	$context['post_box_name'] = $editorOptions['id'];
1173
+
1174
+	// Are there any restrictions on uploading attachments?
1175
+	if (empty($modSettings['shd_attachments_mode']))
1176
+		$modSettings['shd_attachments_mode'] = 'ticket'; // just in case we never actually go to the admin page!!
1177
+
1178
+	$context['allowed_extensions'] = strtr($modSettings['attachmentExtensions'], array(',' => ', '));
1179
+	$context['attachment_restrictions'] = array();
1180
+
1181
+	if (!empty($modSettings['attachmentNumPerPostLimit']) && $modSettings['shd_attachments_mode'] != 'ticket')
1182
+		$context['attachment_restrictions'][] = sprintf($txt['attach_restrict_attachmentNumPerPostLimit'], $modSettings['attachmentNumPerPostLimit']);
1183
+
1184
+	if (!empty($modSettings['attachmentPostLimit']) && $modSettings['shd_attachments_mode'] != 'ticket')
1185
+		$context['attachment_restrictions'][] = sprintf($txt['attach_restrict_attachmentPostLimit'], $modSettings['attachmentPostLimit']);
1186
+
1187
+	if (!empty($modSettings['attachmentSizeLimit']))
1188
+		$context['attachment_restrictions'][] = sprintf($txt['attach_restrict_attachmentSizeLimit'], $modSettings['attachmentSizeLimit']);
1189
+}
1190
+
1191
+function shd_prepare_reply_context()
1192
+{
1193
+	global $settings, $txt, $modSettings, $scripturl, $options, $user_info, $smcFunc;
1194
+	global $memberContext, $context, $reply_request;
1195
+
1196
+	if (empty($reply_request))
1197
+		return false;
1198
+
1199
+	$message = $smcFunc['db_fetch_assoc']($reply_request);
1200
+	if (!$message)
1201
+	{
1202
+		$smcFunc['db_free_result']($reply_request);
1203
+		return false;
1204
+	}
1205
+
1206
+	if (!loadMemberContext($message['id_member'], true))
1207
+	{
1208
+		// Notice this information isn't used anywhere else....
1209
+		$memberContext[$message['id_member']]['name'] = $message['poster_name'];
1210
+		$memberContext[$message['id_member']]['id'] = 0;
1211
+		$memberContext[$message['id_member']]['group'] = $txt['guest_title'];
1212
+		$memberContext[$message['id_member']]['link'] = $message['poster_name'];
1213
+		$memberContext[$message['id_member']]['email'] = $message['poster_email'];
1214
+		$memberContext[$message['id_member']]['show_email'] = showEmailAddress(true, 0);
1215
+		$memberContext[$message['id_member']]['is_guest'] = true;
1216
+		$memberContext[$message['id_member']]['group_stars'] = '';
1217
+	}
1218
+	$memberContext[$message['id_member']]['ip'] = $message['poster_ip'];
1219
+
1220
+	censorText($message['body']);
1221
+	$message['body'] = shd_format_text($message['body'], $message['smileys_enabled'], 'shd_reply_' . $message['id_msg']);
1222
+
1223
+	$output = array(
1224
+		'id' => $message['id_msg'],
1225
+		'member' => &$memberContext[$message['id_member']],
1226
+		'time' => timeformat($message['poster_time']),
1227
+		'timestamp' => forum_time(true, $message['poster_time']),
1228
+		'body' => $message['body'],
1229
+		'is_team' => !empty($context['is_team'][$message['id_member']]),
1230
+		'can_edit' => shd_allowed_to('shd_edit_reply_any') || ($message['id_member'] == $user_info['id'] && shd_allowed_to('shd_edit_reply_own')),
1231
+	);
1232
+
1233
+	if (!empty($message['modified_time']))
1234
+	{
1235
+		$output['modified'] = array(
1236
+			'time' => timeformat($message['modified_time']),
1237
+			'timestamp' => forum_time(true, $message['modified_time']),
1238
+			'id' => !empty($user_profile[$message['modified_member']]) ? $message['modified_member'] : 0,
1239
+			'name' => !empty($user_profile[$message['modified_member']]) ? $user_profile[$message['modified_member']]['real_name'] : $message['modified_name'],
1240
+		);
1241
+		$output['modified']['link'] = shd_profile_link($output['modified']['name'], $output['modified']['id']);
1242
+	}
1243
+
1244
+	return $output;
1245
+}
1246
+
1247
+function shd_load_attachments()
1248
+{
1249
+	global $smcFunc, $context, $modSettings;
1250
+
1251
+	if (empty($context['current_attachments']))
1252
+		$context['current_attachments'] = array();
1253
+
1254
+	if (empty($modSettings['shd_attachments_mode']))
1255
+		$modSettings['shd_attachments_mode'] = 'ticket'; // just in case we never actually go to the admin page!!
1256
+
1257
+	$context['ticket_attach'][$modSettings['shd_attachments_mode']] = array();
1258
+
1259
+	// Get existing attachments
1260
+	$query = shd_db_query('', '
1261
+		SELECT a.id_attach, a.filename
1262
+		FROM {db_prefix}attachments AS a
1263
+			INNER JOIN {db_prefix}helpdesk_attachments AS hda ON (a.id_attach = hda.id_attach)
1264
+		WHERE ' . ($modSettings['shd_attachments_mode'] == 'ticket' ? 'hda.id_ticket = {int:ticket}' : 'hda.id_msg = {int:msg}') . '
1265
+			AND a.attachment_type != {int:thumb}
1266
+		ORDER BY hda.id_attach',
1267
+		array(
1268
+			'msg' => $context['ticket_form']['msg'],
1269
+			'ticket' => $context['ticket_id'],
1270
+			'thumb' => 3,
1271
+		)
1272
+	);
1273
+
1274
+	while ($row = $smcFunc['db_fetch_assoc']($query))
1275
+		$context['current_attachments'][] = array(
1276
+			'id' => $row['id_attach'],
1277
+			'name' => preg_replace('~&amp;#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', htmlspecialchars($row['filename'])),
1278
+		);
1279
+
1280
+	$smcFunc['db_free_result']($query);
1281
+}
1282
+
1283
+function shd_check_attachments()
1284
+{
1285
+	global $modSettings, $smcFunc, $context, $user_info, $txt;
1286
+
1287
+	if (empty($modSettings['shd_attachments_mode']))
1288
+		$modSettings['shd_attachments_mode'] = 'ticket'; // just in case we never actually go to the admin page!!
1289
+
1290
+	if (empty($context['current_attachments']))
1291
+		$context['current_attachments'] = array();
1292
+
1293
+	if (shd_allowed_to('shd_post_attachment'))
1294
+	{
1295
+		if (empty($_SESSION['temp_attachments']))
1296
+			$_SESSION['temp_attachments'] = array();
1297
+
1298
+		if (!empty($modSettings['currentAttachmentUploadDir']))
1299
+		{
1300
+			if (!is_array($modSettings['attachmentUploadDir']))
1301
+				$modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']);
1302
+
1303
+			// Just use the current path for temp files.
1304
+			$current_attach_dir = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']];
1305
+		}
1306
+		else
1307
+			$current_attach_dir = $modSettings['attachmentUploadDir'];
1308
+
1309
+		// If this isn't a new post, check the current attachments.
1310
+		if (($modSettings['shd_attachments_mode'] == 'ticket' && !empty($context['ticket_id'])) || isset($context['ticket_form']['msg']))
1311
+		{
1312
+			$request = shd_db_query('', '
1313
+				SELECT COUNT(*), SUM(size)
1314
+				FROM {db_prefix}attachments AS a
1315
+					INNER JOIN {db_prefix}helpdesk_attachments AS hda ON (a.id_attach = hda.id_attach)
1316
+				WHERE ' . ($modSettings['shd_attachments_mode'] == 'ticket' ? 'hda.id_ticket = {int:ticket}' : 'hda.id_msg = {int:msg}') . '
1317
+					AND attachment_type = {int:attachment_type}',
1318
+				array(
1319
+					'msg' => $context['ticket_form']['msg'],
1320
+					'ticket' => empty($context['ticket_id']) ? 0 : $context['ticket_id'],
1321
+					'attachment_type' => 0,
1322
+				)
1323
+			);
1324
+			list ($quantity, $total_size) = $smcFunc['db_fetch_row']($request);
1325
+			$smcFunc['db_free_result']($request);
1326
+		}
1327
+		else
1328
+		{
1329
+			$quantity = 0;
1330
+			$total_size = 0;
1331
+		}
1332
+
1333
+		$temp_start = 0;
1334
+
1335
+		if (!empty($_SESSION['temp_attachments']))
1336
+			foreach ($_SESSION['temp_attachments'] as $attachID => $name)
1337
+			{
1338
+				$temp_start++;
1339
+
1340
+				if (preg_match('~^post_tmp_' . $user_info['id'] . '_\d+$~', $attachID) == 0)
1341
+				{
1342
+					unset($_SESSION['temp_attachments'][$attachID]);
1343
+					continue;
1344
+				}
1345
+
1346
+				if (!empty($_POST['attach_del']) && !in_array($attachID, $_POST['attach_del']))
1347
+				{
1348
+					$deleted_attachments = true;
1349
+					unset($_SESSION['temp_attachments'][$attachID]);
1350
+					@unlink($current_attach_dir . '/' . $attachID);
1351
+					continue;
1352
+				}
1353
+
1354
+				$quantity++;
1355
+				$total_size += filesize($current_attach_dir . '/' . $attachID);
1356
+
1357
+				$context['current_attachments'][] = array(
1358
+					'name' => $name,
1359
+					'id' => $attachID,
1360
+				);
1361
+			}
1362
+
1363
+		if (!empty($_POST['attach_del']))
1364
+		{
1365
+			$del_temp = array();
1366
+			foreach ($_POST['attach_del'] as $i => $dummy)
1367
+				$del_temp[$i] = (int) $dummy;
1368
+
1369
+			foreach ($context['current_attachments'] as $k => $dummy)
1370
+				if (!in_array($dummy['id'], $del_temp))
1371
+				{
1372
+					$context['current_attachments'][$k]['unchecked'] = true;
1373
+					$deleted_attachments = !isset($deleted_attachments) || is_bool($deleted_attachments) ? 1 : $deleted_attachments + 1;
1374
+					$quantity--;
1375
+				}
1376
+		}
1377
+
1378
+		if (!empty($_FILES))
1379
+		{
1380
+			$_FILES = array_reverse($_FILES);
1381
+			foreach ($_FILES as $uplfile)
1382
+			{
1383
+				if ($uplfile['name'] == '')
1384
+					continue;
1385
+
1386
+				if (!is_uploaded_file($uplfile['tmp_name']) || (@ini_get('open_basedir') == '' && !file_exists($uplfile['tmp_name'])))
1387
+				{
1388
+					checkSubmitOnce('free');
1389
+					fatal_lang_error('attach_timeout', 'critical');
1390
+				}
1391
+
1392
+				if (!empty($modSettings['attachmentSizeLimit']) && $uplfile['size'] > $modSettings['attachmentSizeLimit'] * 1024)
1393
+				{
1394
+					checkSubmitOnce('free');
1395
+					fatal_lang_error('file_too_big', false, array($modSettings['attachmentSizeLimit']));
1396
+				}
1397
+
1398
+				$quantity++;
1399
+				if (!empty($modSettings['attachmentNumPerPostLimit']) && $quantity > $modSettings['attachmentNumPerPostLimit'] && $modSettings['shd_attachments_mode'] != 'ticket')
1400
+				{
1401
+					checkSubmitOnce('free');
1402
+					fatal_lang_error('attachments_limit_per_post', false, array($modSettings['attachmentNumPerPostLimit']));
1403
+				}
1404
+
1405
+				$total_size += $uplfile['size'];
1406
+				if (!empty($modSettings['attachmentPostLimit']) && $total_size > $modSettings['attachmentPostLimit'] * 1024)
1407
+				{
1408
+					checkSubmitOnce('free');
1409
+					fatal_lang_error('file_too_big', false, array($modSettings['attachmentPostLimit']));
1410
+				}
1411
+
1412
+				if (!empty($modSettings['attachmentCheckExtensions']))
1413
+				{
1414
+					if (!in_array(strtolower(substr(strrchr($uplfile['name'], '.'), 1)), explode(',', strtolower($modSettings['attachmentExtensions']))))
1415
+					{
1416
+						checkSubmitOnce('free');
1417
+						fatal_error($uplfile['name'] . '.<br />' . $txt['cant_upload_type'] . ' ' . $modSettings['attachmentExtensions'] . '.', false);
1418
+					}
1419
+				}
1420
+
1421
+				if (!empty($modSettings['attachmentDirSizeLimit']))
1422
+				{
1423
+					// Make sure the directory isn't full.
1424
+					$dirSize = 0;
1425
+					$dir = @opendir($current_attach_dir) or fatal_lang_error('cant_access_upload_path', 'critical');
1426
+					while ($file = readdir($dir))
1427
+					{
1428
+						if ($file == '.' || $file == '..')
1429
+							continue;
1430
+
1431
+						if (preg_match('~^post_tmp_\d+_\d+$~', $file) != 0)
1432
+						{
1433
+							// Temp file is more than 5 hours old!
1434
+							if (filemtime($current_attach_dir . '/' . $file) < time() - 18000)
1435
+								@unlink($current_attach_dir . '/' . $file);
1436
+							continue;
1437
+						}
1438
+
1439
+						$dirSize += filesize($current_attach_dir . '/' . $file);
1440
+					}
1441
+					closedir($dir);
1442
+
1443
+					// Too big!  Maybe you could zip it or something...
1444
+					if ($uplfile['size'] + $dirSize > $modSettings['attachmentDirSizeLimit'] * 1024)
1445
+						fatal_lang_error('ran_out_of_space');
1446
+				}
1447
+
1448
+				if (!is_writable($current_attach_dir))
1449
+					fatal_lang_error('attachments_no_write', 'critical');
1450
+
1451
+				$attachID = 'post_tmp_' . $user_info['id'] . '_' . $temp_start++;
1452
+				$_SESSION['temp_attachments'][$attachID] = basename($uplfile['name']);
1453
+				$context['current_attachments'][] = array(
1454
+					'name' => basename($uplfile['name']),
1455
+					'id' => $attachID,
1456
+					'approved' => 1,
1457
+				);
1458
+
1459
+				$destName = $current_attach_dir . '/' . $attachID;
1460
+
1461
+				if (!move_uploaded_file($uplfile['tmp_name'], $destName))
1462
+					fatal_lang_error('attach_timeout', 'critical');
1463
+				@chmod($destName, 0644);
1464
+			}
1465
+		}
1466
+	}
1467
+
1468
+	// So, while we're here, how many attachments can we have?
1469
+	$context['ticket_form']['num_allowed_attachments'] = empty($modSettings['attachmentNumPerPostLimit']) || $modSettings['shd_attachments_mode'] == 'ticket' ? -1 : $modSettings['attachmentNumPerPostLimit'];
1470
+}
1471
+
1472
+function shd_handle_attachments()
1473
+{
1474
+	global $modSettings, $smcFunc, $context, $user_info, $sourcedir, $txt;
1475
+
1476
+	if (!shd_allowed_to('shd_post_attachment'))
1477
+		return;
1478
+
1479
+	if (empty($modSettings['shd_attachments_mode']))
1480
+		$modSettings['shd_attachments_mode'] = 'ticket'; // just in case we never actually go to the admin page!!
1481
+
1482
+	$attachIDs = array();
1483
+
1484
+	// Check if they are trying to delete any current attachments....
1485
+	if (isset($_POST['attach_del']))
1486
+	{
1487
+		$del_temp = array();
1488
+		foreach ($_POST['attach_del'] as $i => $dummy)
1489
+			$del_temp[$i] = (int) $dummy;
1490
+
1491
+		// First, get them from the other table
1492
+		$query = shd_db_query('', '
1493
+			SELECT a.id_attach
1494
+			FROM {db_prefix}attachments AS a
1495
+				INNER JOIN {db_prefix}helpdesk_attachments AS hda ON (hda.id_attach = a.id_attach)
1496
+			WHERE ' . ($modSettings['shd_attachments_mode'] == 'ticket' ? 'hda.id_ticket = {int:ticket}' : 'hda.id_msg = {int:msg}'),
1497
+			array(
1498
+				'msg' => $context['ticket_form']['msg'],
1499
+				'ticket' => $context['ticket_id'],
1500
+			)
1501
+		);
1502
+
1503
+		$attachments = array();
1504
+		while ($row = $smcFunc['db_fetch_assoc']($query))
1505
+			$attachments[] = $row['id_attach'];
1506
+
1507
+		$smcFunc['db_free_result']($query);
1508
+
1509
+		// OK, so attachments = full list of attachments on this post, del_temp is list of ones to keep, so look for the ones that aren't in both lists
1510
+		$del_temp = array_diff($attachments, $del_temp);
1511
+
1512
+		if (!empty($del_temp))
1513
+		{
1514
+			require_once($sourcedir . '/ManageAttachments.php');
1515
+			$attachmentQuery = array(
1516
+				'attachment_type' => 0,
1517
+				'id_msg' => 0,
1518
+				'id_attach' => $del_temp,
1519
+			);
1520
+			removeAttachments($attachmentQuery);
1521
+		}
1522
+	}
1523
+
1524
+	// ...or attach a new file...
1525
+	if (!empty($_FILES) || !empty($_SESSION['temp_attachments']))
1526
+	{
1527
+		if (!empty($FILES))
1528
+			$_FILES = array_reverse($_FILES);
1529
+
1530
+		shd_is_allowed_to('shd_post_attachment');
1531
+
1532
+		// Make sure we're uploading to the right place.
1533
+		if (!empty($modSettings['currentAttachmentUploadDir']))
1534
+		{
1535
+			if (!is_array($modSettings['attachmentUploadDir']))
1536
+				$modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']);
1537
+
1538
+			// The current directory, of course!
1539
+			$current_attach_dir = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']];
1540
+		}
1541
+		else
1542
+			$current_attach_dir = $modSettings['attachmentUploadDir'];
1543
+
1544
+		// If this isn't a new post, check the current attachments.
1545
+		if (isset($_REQUEST['msg']) || !empty($context['ticket_id']))
1546
+		{
1547
+			$request = shd_db_query('', '
1548
+				SELECT COUNT(*), SUM(size)
1549
+				FROM {db_prefix}attachments AS a
1550
+					INNER JOIN {db_prefix}helpdesk_attachments AS hda ON (a.id_attach = hda.id_attach)
1551
+				WHERE ' . ($modSettings['shd_attachments_mode'] == 'ticket' ? 'hda.id_ticket = {int:ticket}' : 'hda.id_msg = {int:msg}') . '
1552
+					AND attachment_type = {int:attachment_type}',
1553
+				array(
1554
+					'msg' => $context['ticket_form']['msg'],
1555
+					'ticket' => $context['ticket_id'],
1556
+					'attachment_type' => 0,
1557
+				)
1558
+			);
1559
+			list ($quantity, $total_size) = $smcFunc['db_fetch_row']($request);
1560
+			$smcFunc['db_free_result']($request);
1561
+		}
1562
+		else
1563
+		{
1564
+			$quantity = 0;
1565
+			$total_size = 0;
1566
+		}
1567
+
1568
+		if (!empty($_SESSION['temp_attachments']))
1569
+			foreach ($_SESSION['temp_attachments'] as $attachID => $name)
1570
+			{
1571
+				if (preg_match('~^post_tmp_' . $user_info['id'] . '_\d+$~', $attachID) == 0)
1572
+					continue;
1573
+
1574
+				if (!empty($_POST['attach_del']) && !in_array($attachID, $_POST['attach_del']))
1575
+				{
1576
+					unset($_SESSION['temp_attachments'][$attachID]);
1577
+					@unlink($current_attach_dir . '/' . $attachID);
1578
+					continue;
1579
+				}
1580
+
1581
+				$_FILES['attachment' . $attachID]['tmp_name'] = $attachID;
1582
+				$_FILES['attachment' . $attachID]['name'] = $name;
1583
+				$_FILES['attachment' . $attachID]['size'] = filesize($current_attach_dir . '/' . $attachID);
1584
+				list ($_FILES['attachment' . $attachID]['width'], $_FILES['attachment' . $attachID]['height']) = @getimagesize($current_attach_dir . '/' . $attachID);
1585
+
1586
+				unset($_SESSION['temp_attachments'][$attachID]);
1587
+			}
1588
+
1589
+		foreach ($_FILES as $uplfile)
1590
+		{
1591
+			if ($uplfile['name'] == '')
1592
+				continue;
1593
+
1594
+			// Have we reached the maximum number of files we are allowed?
1595
+			$quantity++;
1596
+
1597
+			$file_limit = (!empty($modSettings['attachmentNumPerPostLimit']) && $modSettings['shd_attachments_mode'] != 'ticket') ? $modSettings['attachmentNumPerPostLimit'] : $quantity + 1;
1598
+
1599
+			if ($quantity > $file_limit)
1600
+			{
1601
+				checkSubmitOnce('free');
1602
+				fatal_lang_error('attachments_limit_per_post', false, array($modSettings['attachmentNumPerPostLimit']));
1603
+			}
1604
+
1605
+			// Check the total upload size for this post...
1606
+			$total_size += $uplfile['size'];
1607
+			$size_limit = (!empty($modSettings['attachmentPostLimit']) && $modSettings['shd_attachments_mode'] != 'ticket') ? $modSettings['attachmentPostLimit'] * 1024 : $total_size + 1024;
1608
+
1609
+			if ($total_size > $size_limit)
1610
+			{
1611
+				checkSubmitOnce('free');
1612
+				fatal_lang_error('file_too_big', false, array($modSettings['attachmentPostLimit']));
1613
+			}
1614
+
1615
+			$attachmentOptions = array(
1616
+				'post' => 0,
1617
+				'poster' => $user_info['id'],
1618
+				'name' => $uplfile['name'],
1619
+				'tmp_name' => $uplfile['tmp_name'],
1620
+				'size' => $uplfile['size'],
1621
+			);
1622
+
1623
+			if (createAttachment($attachmentOptions))
1624
+			{
1625
+				$attachIDs[] = $attachmentOptions['id'];
1626
+				if (!empty($attachmentOptions['thumb']))
1627
+					$attachIDs[] = $attachmentOptions['thumb'];
1628
+			}
1629
+			else
1630
+			{
1631
+				if (in_array('could_not_upload', $attachmentOptions['errors']))
1632
+				{
1633
+					checkSubmitOnce('free');
1634
+					fatal_lang_error('attach_timeout', 'critical');
1635
+				}
1636
+				if (in_array('too_large', $attachmentOptions['errors']))
1637
+				{
1638
+					checkSubmitOnce('free');
1639
+					fatal_lang_error('file_too_big', false, array($modSettings['attachmentSizeLimit']));
1640
+				}
1641
+				if (in_array('bad_extension', $attachmentOptions['errors']))
1642
+				{
1643
+					checkSubmitOnce('free');
1644
+					fatal_error($attachmentOptions['name'] . '.<br />' . $txt['cant_upload_type'] . ' ' . strtr($modSettings['attachmentExtensions'], array(',' => ', ')) . '.', false);
1645
+				}
1646
+				if (in_array('directory_full', $attachmentOptions['errors']))
1647
+				{
1648
+					checkSubmitOnce('free');
1649
+					fatal_lang_error('ran_out_of_space', 'critical');
1650
+				}
1651
+				if (in_array('bad_filename', $attachmentOptions['errors']))
1652
+				{
1653
+					checkSubmitOnce('free');
1654
+					fatal_error(basename($attachmentOptions['name']) . '.<br />' . $txt['restricted_filename'] . '.', 'critical');
1655
+				}
1656
+				if (in_array('taken_filename', $attachmentOptions['errors']))
1657
+				{
1658
+					checkSubmitOnce('free');
1659
+					fatal_lang_error('filename_exists');
1660
+				}
1661
+			}
1662
+		}
1663
+	}
1664
+
1665
+	return $attachIDs;
1666
+}
1667
+
1668
+?>
0 1669
\ No newline at end of file
... ...
@@ -0,0 +1,872 @@
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: 1.0 Felidae                             #
17
+# File Info: SimpleDesk-TopicTicketMove.php / 1.0 Felidae     #
18
+###############################################################
19
+
20
+/**
21
+ *	This file handles displaying the user options for, and subsequently enacting, tickets being moved
22
+ *	to/from the helpdesk, from/to forum threads.
23
+ *
24
+ *	@package source
25
+ *	@since 1.0
26
+*/
27
+
28
+if (!defined('SMF'))
29
+	die('Hacking attempt...');
30
+
31
+/**
32
+ *	Load the form up for asking users what board they want to go to.
33
+ *
34
+ *	Validates the user permission and session, then displays a list of boards the user can create new topics in.
35
+ *
36
+ *	Accessed through action=helpdesk;sa=tickettotopic;ticket=x;sessvar=sessid
37
+ *
38
+ *	@see shd_tickettotopic2()
39
+ *	@since 1.0
40
+*/
41
+function shd_tickettotopic()
42
+{
43
+	global $smcFunc, $context, $user_info, $sourcedir, $txt, $modSettings, $scripturl;
44
+
45
+	checkSession('get');
46
+
47
+	if (!shd_allowed_to('shd_ticket_to_topic') || !empty($modSettings['shd_helpdesk_only']))
48
+		fatal_lang_error('shd_cannot_move_ticket', false);
49
+
50
+	if (empty($context['ticket_id']))
51
+		fatal_lang_error('shd_no_ticket');
52
+
53
+	// Get ticket details - and kick it out if they shouldn't be able to see it.
54
+	$query = shd_db_query('', '
55
+		SELECT id_member_started, id_member_assigned, private, subject, deleted_replies
56
+		FROM {db_prefix}helpdesk_tickets AS hdt
57
+		WHERE {query_see_ticket} AND id_ticket = {int:ticket}',
58
+		array(
59
+			'ticket' => $context['ticket_id'],
60
+		)
61
+	);
62
+
63
+	if ($row = $smcFunc['db_fetch_row']($query))
64
+	{
65
+		list($ticket_starter, $ticket_owner, $private, $subject, $deleted_replies) = $row;
66
+		$smcFunc['db_free_result']($query);
67
+	}
68
+	else
69
+	{
70
+		$smcFunc['db_free_result']($query);
71
+		fatal_lang_error('shd_no_ticket');
72
+	}
73
+
74
+	// Hang on... are there any deleted replies?
75
+	if ($deleted_replies > 0)
76
+	{
77
+		if (shd_allowed_to('shd_access_recyclebin'))
78
+			$context['deleted_prompt'] = true;
79
+		else
80
+			fatal_lang_error('shd_cannot_move_ticket_with_deleted');
81
+	}
82
+
83
+	// Build the linktree
84
+	$context['linktree'][] = array(
85
+		'url' => $scripturl . '?action=helpdesk;sa=main',
86
+		'name' => $txt['shd_linktree_move_ticket'],
87
+	);
88
+	$context['linktree'][] = array(
89
+		'url' => $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $context['ticket_id'],
90
+		'name' => $subject,
91
+	);
92
+
93
+	// They should only be able to move it to boards they are allowed to post in.
94
+	$allowedboards = boardsAllowedTo('post_new');
95
+	if ($allowedboards[0] == 0)
96
+		$allowedboards = ''; // They can access everything.
97
+
98
+	// All good, now figure out what board(s) they can move it to. (MoveTopic.php style 0-:D)
99
+	$request = shd_db_query('order_by_board_order', '
100
+		SELECT b.id_board, b.name, b.child_level, c.name AS cat_name, c.id_cat
101
+		FROM {db_prefix}boards AS b
102
+			LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
103
+		WHERE {query_see_board}
104
+			AND b.redirect = {string:blank_redirect}
105
+			' . (!empty($allowedboards) ? 'AND id_board IN({array_int:allowed_boards})' : ''),
106
+		array(
107
+			'blank_redirect' => '',
108
+			'allowed_boards' => $allowedboards,
109
+		)
110
+	);
111
+	$context['boards'] = array();
112
+
113
+	while ($row = $smcFunc['db_fetch_assoc']($request))
114
+	{
115
+		if (!isset($context['categories'][$row['id_cat']]))
116
+			$context['categories'][$row['id_cat']] = array (
117
+				'name' => strip_tags($row['cat_name']),
118
+				'boards' => array(),
119
+			);
120
+
121
+		$context['categories'][$row['id_cat']]['boards'][] = array(
122
+			'id' => $row['id_board'],
123
+			'name' => strip_tags($row['name']),
124
+			'category' => strip_tags($row['cat_name']),
125
+			'child_level' => $row['child_level'],
126
+			'selected' => !empty($_SESSION['move_to_topic']) && $_SESSION['move_to_topic'] == $row['id_board'] && $row['id_board'] != $board,
127
+		);
128
+	}
129
+
130
+	$smcFunc['db_free_result']($request);
131
+
132
+	if (empty($context['categories']))
133
+		fatal_lang_error('shd_moveticket_noboards', false);
134
+
135
+	loadTemplate('SimpleDesk-TicketTopicMove');
136
+	$context['sub_template'] = 'shd_tickettotopic';
137
+	$context['page_title'] = $txt['shd_move_ticket_to_topic'];
138
+	checkSubmitOnce('register');
139
+}
140
+
141
+/**
142
+ *	Receives the form for moving tickets to topics, and actually handles the move.
143
+ *
144
+ *	After checking permissions, and so on, begin to actually move posts.
145
+ *
146
+ *	This is done by invoking SMF's createPost function to make the new thread and repost all the ticket's posts as new thread posts
147
+ *	using defaults for some settings.
148
+ *
149
+ *	Operations:
150
+ *	- check the user can see the board they are linking to
151
+ *	- move the ticket's text as the opening post
152
+ *	- update the ticket row if the post was modified before
153
+ *	- get the rest of the replies
154
+ *	- step through and post, updating for modified details
155
+ *	- send the notification PM if we're doing that
156
+ *	- update the attachments table
157
+ *	- update the action log
158
+ *	- remove the ticket from the DB
159
+ *
160
+ *	@see shd_tickettotopic()
161
+ *	@since 1.0
162
+*/
163
+function shd_tickettotopic2()
164
+{
165
+	global $smcFunc, $context, $txt, $modSettings, $scripturl, $sourcedir;
166
+
167
+	checkSession();
168
+	checkSubmitOnce('check');
169
+
170
+	if (!shd_allowed_to('shd_ticket_to_topic') || !empty($modSettings['shd_helpdesk_only']))
171
+		fatal_lang_error('shd_cannot_move_ticket', false);
172
+
173
+	if (empty($context['ticket_id']))
174
+		fatal_lang_error('shd_no_ticket');
175
+
176
+	if (isset($_POST['send_pm']) && (!isset($_POST['pm_content']) || trim($_POST['pm_content']) == ''))
177
+		fatal_lang_error('shd_move_no_pm', false);
178
+
179
+	// Just in case, are they cancelling?
180
+	if (isset($_REQUEST['cancel']))
181
+		redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id']);
182
+
183
+	require_once($sourcedir . '/Subs-Post.php');
184
+
185
+	// The destination board must be numeric.
186
+	$_POST['toboard'] = (int) $_POST['toboard'];
187
+
188
+	$msg_assoc = array();
189
+
190
+	// This is complex, very complex. Hopefully 5 minutes will be enough...
191
+	@set_time_limit(300);
192
+
193
+	// Make sure they can see the board they are trying to move to (and get whether posts count in the target board).
194
+	$request = shd_db_query('', '
195
+		SELECT b.count_posts, b.name, hdt.subject, hdt.id_member_started, hdtr.body, hdt.id_first_msg, hdtr.smileys_enabled,
196
+		hdtr.modified_time, hdtr.modified_name, hdtr.poster_time, hdtr.id_msg, hdt.deleted_replies
197
+		FROM {db_prefix}boards AS b
198
+			INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hdt.id_ticket = {int:ticket})
199
+			INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hdtr.id_msg = hdt.id_first_msg)
200
+		WHERE {query_see_board}
201
+			AND {query_see_ticket}
202
+			AND b.id_board = {int:to_board}
203
+			AND b.redirect = {string:blank_redirect}
204
+		LIMIT 1',
205
+		array(
206
+			'ticket' => $context['ticket_id'],
207
+			'to_board' => $_POST['toboard'],
208
+			'blank_redirect' => '',
209
+		)
210
+	);
211
+	if ($smcFunc['db_num_rows']($request) == 0)
212
+		fatal_lang_error('no_board');
213
+	else
214
+		list ($pcounter, $board_name, $subject, $owner, $body, $firstmsg, $smileys_enabled, $modified_time, $modified_name, $time, $shd_id_msg, $deleted_replies) = $smcFunc['db_fetch_row']($request);
215
+
216
+	$smcFunc['db_free_result']($request);
217
+
218
+	$context['deleted_prompt'] = false;
219
+	// Hang on... are there any deleted replies?
220
+	if ($deleted_replies > 0)
221
+	{
222
+		if (shd_allowed_to('shd_access_recyclebin'))
223
+		{
224
+			$dr_opts = array('abort', 'delete', 'undelete');
225
+
226
+			$context['deleted_prompt'] = isset($_REQUEST['deleted_replies']) && in_array($_REQUEST['deleted_replies'], $dr_opts) ? $_REQUEST['deleted_replies'] : 'abort';
227
+		}
228
+		else
229
+			fatal_lang_error('shd_cannot_move_ticket_with_deleted');
230
+	}
231
+
232
+	if (!empty($context['deleted_prompt']) && $context['deleted_prompt'] == 'abort')
233
+		redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id'] . ';recycle');
234
+
235
+	// All okay, it seems. Let's go create the topic.
236
+	$msgOptions = array(
237
+		'subject' => '[' . $txt['shd_ticket'] . '] ' . $subject,
238
+		'body' => $body,
239
+		'icon' => 'xx',
240
+		'smileys_enabled' => !empty($smileys_enabled) ? 1 : 0,
241
+	);
242
+	$topicOptions = array(
243
+		'board' => $_POST['toboard'],
244
+		'lock_mode' => 0,
245
+		'mark_as_read' => false,
246
+	);
247
+	$posterOptions = array(
248
+		'id' => $owner,
249
+		'update_post_count' => empty($pcounter),
250
+	);
251
+	createPost($msgOptions, $topicOptions, $posterOptions);
252
+
253
+	// Keep track of SHD msg id to SMF msg id
254
+	$msg_assoc[$shd_id_msg] = $msgOptions['id'];
255
+
256
+	// createPost() doesn't handle modified time and name, so we'll fix that here, along with the poster time.
257
+	if (!empty($modified_time))
258
+	{
259
+		shd_db_query('', '
260
+			UPDATE {db_prefix}messages
261
+			SET
262
+				modified_time = {int:modified_time},
263
+				modified_name = {string:modified_name},
264
+				poster_time = {int:poster_time}
265
+			WHERE id_msg = {int:id_msg}',
266
+				array(
267
+					'id_msg' => $firstmsg,
268
+					'modified_time' => $modified_time,
269
+					'modified_name' => $modified_name,
270
+					'poster_time' => $time,
271
+				)
272
+			);
273
+	}
274
+
275
+	// Topic created, let's dig out the replies and post them in the topic, if there are any.
276
+	if (isset($topicOptions['id']))
277
+	{
278
+		$request = shd_db_query('', '
279
+			SELECT body, id_member, poster_time, poster_name, poster_email, poster_ip, smileys_enabled,
280
+				modified_time, modified_member, modified_name, poster_time, id_msg, message_status
281
+			FROM {db_prefix}helpdesk_ticket_replies AS hdtr
282
+			WHERE id_ticket = {int:ticket}
283
+				AND id_msg > {int:ticket_msg}',
284
+			array(
285
+				'ticket' => $context['ticket_id'],
286
+				'ticket_msg' => $firstmsg,
287
+			)
288
+		);
289
+
290
+		// The ID of the topic we created
291
+		$topic = $topicOptions['id'];
292
+
293
+		if ($smcFunc['db_num_rows']($request) != 0)
294
+		{
295
+			// Now loop through each reply and post it.  Hopefully there aren't too many. *looks at clock*
296
+			while ($row = $smcFunc['db_fetch_assoc']($request))
297
+			{
298
+				if ($row['message_status'] == MSG_STATUS_DELETED && !empty($context['deleted_prompt']) && $context['deleted_prompt'] == 'delete') // we don't want these replies!
299
+					continue;
300
+
301
+				$msgOptions = array(
302
+					'subject' => $txt['response_prefix'] . $subject,
303
+					'body' => $row['body'],
304
+					'icon' => 'xx',
305
+					'smileys_enabled' => !empty($row['smileys_enabled']) ? 1 : 0,
306
+				);
307
+				$topicOptions = array(
308
+					'id' => $topic,
309
+					'board' => $_POST['toboard'],
310
+					'lock_mode' => 0,
311
+					'mark_as_read' => false,
312
+				);
313
+				$posterOptions = array(
314
+					'id' => $row['id_member'],
315
+					'name' => !empty($row['poster_name']) ? $row['poster_name'] : '',
316
+					'email' => !empty($row['poster_email']) ? $row['poster_email'] : '',
317
+					'ip' => !empty($row['poster_ip']) ? $row['poster_ip'] : '',
318
+					'update_post_count' => empty($pcounter),
319
+				);
320
+				createPost($msgOptions, $topicOptions, $posterOptions);
321
+
322
+				// Don't forget to note what id
323
+				$msg_assoc[$row['id_msg']] = $msgOptions['id'];
324
+
325
+				// Meh, createPost() doesn't have any hooks for modified time and user. Let's fix that now.
326
+				if (!empty($row['modified_time']))
327
+				{
328
+					shd_db_query('', '
329
+						UPDATE {db_prefix}messages
330
+						SET
331
+							modified_time = {int:modified_time},
332
+							modified_name = {string:modified_name},
333
+							poster_time = {int:poster_time}
334
+						WHERE id_msg = {int:id_msg}',
335
+						array(
336
+							'id_msg' => $msgOptions['id'],
337
+							'modified_time' => $row['modified_time'],
338
+							'modified_name' => $row['modified_name'],
339
+							'poster_time' => $row['poster_time'],
340
+						)
341
+					);
342
+				}
343
+			}
344
+		}
345
+
346
+		// Topic: check; Replies: check; Notfiy the ticket starter, if desired.
347
+		if (isset($_POST['send_pm']))
348
+		{
349
+			$request = shd_db_query('pm_find_username', '
350
+				SELECT id_member, real_name
351
+				FROM {db_prefix}members
352
+				WHERE id_member = {int:user}
353
+				LIMIT 1',
354
+				array(
355
+					'user' => $owner,
356
+				)
357
+			);
358
+			list ($userid,$username) = $smcFunc['db_fetch_row']($request);
359
+			$smcFunc['db_free_result']($request);
360
+
361
+			// Fix the content
362
+			$replacements = array(
363
+				'{user}' => $username,
364
+				'{subject}' => $subject,
365
+				'{board}' => '[url=' . $scripturl . '?board=' . $_POST['toboard'] . '.0]' . $board_name . '[/url]',
366
+				'{link}' => $scripturl . '?topic=' . $topic . '.0',
367
+			);
368
+			$message = str_replace(array_keys($replacements), array_values($replacements),$_POST['pm_content']);
369
+
370
+			$recipients = array(
371
+				'to' => array($owner),
372
+				'bcc' => array()
373
+			);
374
+
375
+			sendpm($recipients,$txt['shd_ticket_moved_subject'],$message);
376
+		}
377
+
378
+		// Right, time to do all the attachment fussing too
379
+		if (!empty($msg_assoc))
380
+		{
381
+			$attachIDs = array();
382
+			$query = shd_db_query('', '
383
+				SELECT id_attach, id_msg
384
+				FROM {db_prefix}helpdesk_attachments
385
+				WHERE id_msg IN ({array_int:msgs})',
386
+				array(
387
+					'msgs' => array_keys($msg_assoc),
388
+				)
389
+			);
390
+
391
+			while ($row = $smcFunc['db_fetch_assoc']($query))
392
+				$attachIDs[] = $row;
393
+
394
+			$smcFunc['db_free_result']($query);
395
+
396
+			if (!empty($attachIDs))
397
+			{
398
+				// 1. Update all the attachments in the master table; this is the bit that hurts since it can't be done without
399
+				// a query per row :(
400
+				foreach ($attachIDs as $attach)
401
+				{
402
+					shd_db_query('', '
403
+						UPDATE {db_prefix}attachments
404
+						SET id_msg = {int:new_msg}
405
+						WHERE id_attach = {int:attach}',
406
+						array(
407
+							'attach' => $attach['id_attach'],
408
+							'new_msg' => $msg_assoc[$attach['id_msg']],
409
+						)
410
+					);
411
+				}
412
+
413
+				// 2. Remove the entries from the SD table since we don't need them no more
414
+				shd_db_query('', '
415
+					DELETE FROM {db_prefix}helpdesk_attachments
416
+					WHERE id_msg IN ({array_int:msgs})',
417
+					array(
418
+						'msgs' => array_keys($msg_assoc),
419
+					)
420
+				);
421
+			}
422
+		}
423
+
424
+		// Well, it's possible there are some phantom attachments on deleted replies that need to disappear
425
+		if (!empty($context['deleted_replies']) && $context['deleted_replies'] == 'delete')
426
+		{
427
+			$query = shd_db_query('', '
428
+				SELECT id_msg
429
+				FROM {db_prefix}helpdesk_attachments AS hda
430
+					INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hda.id_msg = hdtr.id_msg)
431
+				WHERE id_ticket = {int:ticket}
432
+					AND hdtr.message_status = {int:deleted}',
433
+				array(
434
+					'ticket' => $context['ticket_id'],
435
+					'deleted' => MSG_STATUS_DELETED,
436
+				)
437
+			);
438
+
439
+			if ($smcFunc['db_num_rows']($query) > 0)
440
+			{
441
+				$msgs = array();
442
+				while ($row = $smcFunc['db_fetch_row']($query))
443
+					$msgs[] = $row[0];
444
+
445
+				if (!empty($msgs))
446
+				{
447
+					// Get rid of the parents
448
+					require_once($sourcedir . '/ManageAttachments.php');
449
+					$attachmentQuery = array(
450
+						'attachment_type' => 0,
451
+						'id_msg' => 0,
452
+						'id_attach' => $msgs,
453
+					);
454
+					removeAttachments($attachmentQuery);
455
+
456
+					// Get rid of the remainder in hda table
457
+					shd_db_query('', '
458
+						DELETE FROM {db_prefix}helpdesk_attachments
459
+						WHERE id_ticket = {int:ticket}',
460
+						array(
461
+							'ticket' => $context['ticket_id'],
462
+						)
463
+					);
464
+				}
465
+			}
466
+
467
+			$smcFunc['db_free_result']($query);
468
+		}
469
+
470
+		// Now we'll add this to the log.
471
+		$log_params = array(
472
+			'subject' => $subject,
473
+			'board_id' => $_POST['toboard'],
474
+			'board_name' => $board_name,
475
+			'ticket' => $topic,
476
+		);
477
+		shd_log_action('tickettotopic', $log_params);
478
+
479
+		// Lastly, delete the ticket from the database.
480
+		shd_db_query('', '
481
+			DELETE FROM {db_prefix}helpdesk_tickets
482
+			WHERE id_ticket = {int:ticket}
483
+			LIMIT 1',
484
+			array(
485
+				'ticket' => $context['ticket_id'],
486
+			)
487
+		);
488
+		// And the replies, too.
489
+		shd_db_query('', '
490
+			DELETE FROM {db_prefix}helpdesk_ticket_replies
491
+			WHERE id_ticket = {int:ticket}',
492
+			array(
493
+				'ticket' => $context['ticket_id'],
494
+			)
495
+		);
496
+
497
+	}
498
+	else
499
+		fatal_lang_error('shd_move_topic_not_created',false);
500
+
501
+	// Clear our cache
502
+	shd_clear_active_tickets($owner);
503
+
504
+	// Send them to the topic.
505
+	redirectexit('topic=' . $topic . '.0');
506
+
507
+}
508
+
509
+/**
510
+ *	Load the form up for asking users whether to send a personal message (and what message) to the topic/ticket starter or not. Most of the rest of this function is about loading the data to make the page well-rounded (like the subject for breadcrumbs)
511
+ *
512
+ *	Validates the user permission and session, of course.
513
+ *
514
+ *	Accessed through action=helpdesk;sa=topictoticket;topic=x;sessvar=sessid
515
+ *
516
+ *	@see shd_topictoticket2()
517
+ *	@since 1.0
518
+*/
519
+function shd_topictoticket()
520
+{
521
+	global $smcFunc, $context, $user_info, $sourcedir, $txt, $modSettings, $scripturl;
522
+
523
+	checkSession('get');
524
+
525
+	if (!shd_allowed_to('shd_topic_to_ticket') || !empty($modSettings['shd_helpdesk_only']))
526
+		fatal_lang_error('shd_cannot_move_topic', false);
527
+
528
+	if (empty($_REQUEST['topic']))
529
+		fatal_lang_error('shd_no_topic');
530
+
531
+	$context['topic_id'] = (int) $_REQUEST['topic'];
532
+
533
+	// Get topic details
534
+	$query = shd_db_query('', '
535
+		SELECT m.subject
536
+		FROM {db_prefix}topics AS t
537
+			INNER JOIN {db_prefix}messages AS m ON (m.id_topic = {int:topic})
538
+			INNER JOIN {db_prefix}boards AS b ON (t.id_board = b.id_board)
539
+		WHERE {query_see_board} AND t.id_topic = {int:topic}',
540
+		array(
541
+			'topic' => $context['topic_id'],
542
+		)
543
+	);
544
+
545
+	if ($row = $smcFunc['db_fetch_row']($query))
546
+	{
547
+		list($subject) = $row;
548
+		$smcFunc['db_free_result']($query);
549
+	}
550
+	else
551
+	{
552
+		$smcFunc['db_free_result']($query);
553
+		fatal_lang_error('shd_no_topic');
554
+	}
555
+
556
+	// Build the linktree
557
+	$context['linktree'][] = array(
558
+		'url' => $scripturl . '?action=helpdesk;sa=main',
559
+		'name' => $txt['shd_linktree_move_topic'],
560
+	);
561
+	$context['linktree'][] = array(
562
+		'url' => $scripturl . '?topic=' . $context['topic_id'] . '.0',
563
+		'name' => $subject,
564
+	);
565
+
566
+	loadTemplate('SimpleDesk-TicketTopicMove');
567
+	$context['sub_template'] = 'shd_topictoticket';
568
+	$context['page_title'] = $txt['shd_move_topic_to_ticket'];
569
+	checkSubmitOnce('register');
570
+}
571
+
572
+/**
573
+ *	Handles moving a topic into the helpdesk.
574
+ *
575
+ *	After checking permissions, and so on, begin to actually move posts.
576
+ *
577
+ *	Broadly this is done using {@link shd_create_ticket_post()}, which has hooks specifically to deal with post modification times (written in specifically to ease this function's workload)
578
+ *
579
+ *	Operations:
580
+ *	- get the topic information (and checking topic access permission in the process)
581
+ *	- identify the status of the topic (new/with staff/with user)
582
+ *	- create the new ticket from these details
583
+ *	- assuming there are replies, query for them
584
+ *	- step through and repost
585
+ *	- send the notification PM if we're doing that
586
+ *	- update the attachments table
587
+ *	- update the action log
588
+ *	- remove the topic from the forum
589
+ *
590
+ *	@see shd_topictoticket()
591
+ *	@since 1.0
592
+*/
593
+function shd_topictoticket2()
594
+{
595
+	global $smcFunc, $context, $txt, $modSettings, $scripturl, $sourcedir;
596
+
597
+	checkSession();
598
+	checkSubmitOnce('check');
599
+
600
+	if (!shd_allowed_to('shd_topic_to_ticket') || !empty($modSettings['shd_helpdesk_only']))
601
+		fatal_lang_error('shd_cannot_move_topic', false);
602
+
603
+	if (empty($_REQUEST['topic']))
604
+		fatal_lang_error('shd_no_topic');
605
+
606
+	$context['topic_id'] = (int) $_REQUEST['topic'];
607
+
608
+	// Just in case, are they cancelling?
609
+	if (isset($_REQUEST['cancel']))
610
+		redirectexit('topic=' . $context['topic_id']);
611
+
612
+	if (isset($_POST['send_pm']) && (!isset($_POST['pm_content']) || trim($_POST['pm_content']) == ''))
613
+		fatal_lang_error('shd_move_no_pm_topic', false);
614
+
615
+	require_once($sourcedir . '/Subs-SimpleDeskPost.php');
616
+
617
+	// Fetch the topic information.
618
+	$request = shd_db_query('', '
619
+		SELECT m.subject, t.id_board, t.id_member_started, m.body, t.id_first_msg, m.smileys_enabled, t.id_member_updated, t.num_replies,
620
+		m.poster_email, m.poster_name, m.poster_ip, m.poster_time, m.modified_time, m.modified_name, m.id_msg
621
+		FROM {db_prefix}topics AS t
622
+			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
623
+			INNER JOIN {db_prefix}boards AS b ON (t.id_board = b.id_board)
624
+		WHERE {query_see_board} AND t.id_topic = {int:topic}
625
+		LIMIT 1',
626
+		array(
627
+			'topic' => $context['topic_id'],
628
+		)
629
+	);
630
+	if ($smcFunc['db_num_rows']($request) == 0)
631
+		fatal_lang_error('shd_move_ticket_not_created');
632
+	else
633
+		list ($subject, $board, $owner, $body, $firstmsg, $smileys_enabled, $memberupdated, $numreplies, $postername, $posteremail, $posterip, $postertime, $modified_time, $modified_name, $smf_id_msg) = $smcFunc['db_fetch_row']($request);
634
+
635
+	$smcFunc['db_free_result']($request);
636
+
637
+	// Figure out what the status of the ticket should be.
638
+	$status = shd_determine_status('topictoticket', $owner, $memberupdated, $numreplies);
639
+
640
+	// All okay, it seems. Let's go create the ticket.
641
+	$msg_assoc = array();
642
+
643
+	$msgOptions = array(
644
+		'body' => $body,
645
+		'smileys_enabled' => !empty($smileys_enabled) ? 1 : 0,
646
+		'modified' => array(
647
+			'time' => $modified_time,
648
+			'name' => $modified_name,
649
+		),
650
+		'time' => $postertime,
651
+	);
652
+	$ticketOptions = array(
653
+		'subject' => $subject,
654
+		'mark_as_read' => false,
655
+		'private' => false,
656
+		'status' => $status,
657
+		'urgency' => 0,
658
+		'assigned' => 0,
659
+	);
660
+	$posterOptions = array(
661
+		'id' => $owner,
662
+		'name' => $postername,
663
+		'email' => $posteremail,
664
+		'ip' => $posterip,
665
+	);
666
+	shd_create_ticket_post($msgOptions, $ticketOptions, $posterOptions);
667
+	$msg_assoc[$smf_id_msg] = $msgOptions['id'];
668
+
669
+	// Ticket created, let's dig out the replies and post them in the ticket, if there are any.
670
+	if (isset($ticketOptions['id']))
671
+	{
672
+		$request = shd_db_query('', '
673
+			SELECT body, id_member, poster_time, poster_name, poster_email, poster_ip, smileys_enabled, id_msg
674
+			FROM {db_prefix}messages
675
+			WHERE id_topic = {int:topic}
676
+			AND id_msg != {int:topic_msg}',
677
+			array(
678
+				'topic' => $context['topic_id'],
679
+				'topic_msg' => $firstmsg,
680
+			)
681
+		);
682
+
683
+		$num_replies = $smcFunc['db_num_rows']($request) + 1; // Plus one since we want to count the main ticket post as well.
684
+
685
+		// The ID of the ticket we created
686
+		$ticket = $ticketOptions['id'];
687
+
688
+		if ($smcFunc['db_num_rows']($request) != 0)
689
+		{
690
+			// Now loop through each reply and post it.  Hopefully there aren't too many. *looks at clock*
691
+			while ($row = $smcFunc['db_fetch_assoc']($request))
692
+			{
693
+				$msgOptions = array(
694
+					'body' => $row['body'],
695
+					'smileys_enabled' => !empty($row['smileys_enabled']) ? 1 : 0,
696
+				);
697
+				$ticketOptions = array(
698
+					'id' => $ticket,
699
+					'mark_as_read' => false,
700
+				);
701
+				$posterOptions = array(
702
+					'id' => $row['id_member'],
703
+					'name' => !empty($row['poster_name']) ? $row['poster_name'] : '',
704
+					'email' => !empty($row['poster_email']) ? $row['poster_email'] : '',
705
+					'ip' => !empty($row['poster_ip']) ? $row['poster_ip'] : '',
706
+				);
707
+				shd_create_ticket_post($msgOptions, $ticketOptions, $posterOptions);
708
+				$msg_assoc[$row['id_msg']] = $msgOptions['id'];
709
+			}
710
+		}
711
+
712
+		// Ticket: check; Replies: check; Notfiy the topic starter, if desired.
713
+		if (isset($_POST['send_pm']))
714
+		{
715
+			require_once($sourcedir . '/Subs-Post.php');
716
+
717
+			$request = shd_db_query('pm_find_username', '
718
+				SELECT id_member, real_name
719
+				FROM {db_prefix}members
720
+				WHERE id_member = {int:user}
721
+				LIMIT 1',
722
+				array(
723
+					'user' => $owner,
724
+				)
725
+			);
726
+			list ($userid,$username) = $smcFunc['db_fetch_row']($request);
727
+			$smcFunc['db_free_result']($request);
728
+
729
+			// Fix the content
730
+			$replacements = array(
731
+				'{user}' => $username,
732
+				'{subject}' => $subject,
733
+				'{link}' => $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $ticket,
734
+			);
735
+			$message = str_replace(array_keys($replacements), array_values($replacements),$_POST['pm_content']);
736
+
737
+			$recipients = array(
738
+				'to' => array($owner),
739
+				'bcc' => array()
740
+			);
741
+
742
+			sendpm($recipients,$txt['shd_ticket_moved_subject_topic'],$message);
743
+		}
744
+
745
+		// And now for something completely different: attachments
746
+
747
+		if (!empty($msg_assoc))
748
+		{
749
+			// 1. Get all the attachments for these messages from the attachments table
750
+			$attachIDs = array();
751
+			$query = shd_db_query('', '
752
+				SELECT id_attach, id_msg
753
+				FROM {db_prefix}attachments
754
+				WHERE id_msg IN ({array_int:smf_msgs})',
755
+				array(
756
+					'smf_msgs' => array_keys($msg_assoc),
757
+				)
758
+			);
759
+
760
+			while ($row = $smcFunc['db_fetch_assoc']($query))
761
+				$attachIDs[] = $row;
762
+			$smcFunc['db_free_result']($query);
763
+
764
+			if (!empty($attachIDs))
765
+			{
766
+				// 2. Do the switch
767
+				// 2.1. Add them to SD's tables
768
+				$array = array();
769
+				foreach ($attachIDs as $attach)
770
+					$array[] = array($attach['id_attach'], $ticket, $msg_assoc[$attach['id_msg']]);
771
+
772
+				$smcFunc['db_insert']('replace',
773
+					'{db_prefix}helpdesk_attachments',
774
+					array(
775
+						'id_attach' => 'int', 'id_ticket' => 'int', 'id_msg' => 'int',
776
+					),
777
+					$array,
778
+					array(
779
+						'id_attach',
780
+					)
781
+				);
782
+
783
+				// 2.2. "Remove" them from SMF's table
784
+				shd_db_query('', '
785
+					UPDATE {db_prefix}attachments
786
+					SET id_msg = 0
787
+					WHERE id_msg IN ({array_int:smf_msgs})',
788
+					array(
789
+						'smf_msgs' => array_keys($msg_assoc),
790
+					)
791
+				);
792
+			}
793
+		}
794
+
795
+		// Now we'll add this to the log.
796
+		$log_params = array(
797
+			'subject' => $subject,
798
+			'ticket' => $ticket,
799
+		);
800
+		shd_log_action('topictoticket', $log_params);
801
+
802
+		// Update post counts.
803
+		$request = shd_db_query('', '
804
+			SELECT id_member
805
+			FROM {db_prefix}messages
806
+			WHERE id_topic = {int:topic}',
807
+			array(
808
+				'topic' => $context['topic_id'],
809
+			)
810
+		);
811
+		$posters = array();
812
+		while ($row = $smcFunc['db_fetch_assoc']($request))
813
+		{
814
+			if (!isset($posters[$row['id_member']]))
815
+				$posters[$row['id_member']] = 0;
816
+
817
+			$posters[$row['id_member']]++;
818
+		}
819
+		$smcFunc['db_free_result']($request);
820
+
821
+		foreach ($posters as $id_member => $posts)
822
+		{
823
+			updateMemberData($id_member, array('posts' => 'posts - ' . $posts));
824
+		}
825
+
826
+		// Lastly, delete the topic from the database.
827
+		shd_db_query('', '
828
+			DELETE FROM {db_prefix}topics
829
+			WHERE id_topic = {int:topic}
830
+			LIMIT 1',
831
+			array(
832
+				'topic' => $context['topic_id'],
833
+			)
834
+		);
835
+		// And the replies, too.
836
+		shd_db_query('', '
837
+			DELETE FROM {db_prefix}messages
838
+			WHERE id_topic = {int:topic}',
839
+			array(
840
+				'topic' => $context['topic_id'],
841
+			)
842
+		);
843
+
844
+		// Update the stats.
845
+		require_once($sourcedir . '/Subs-Post.php');
846
+		updateStats('message');
847
+		updateStats('topic');
848
+		updateLastMessages($board);
849
+
850
+		// Update board post counts.
851
+		shd_db_query('', '
852
+			UPDATE {db_prefix}boards
853
+			SET num_topics = num_topics - 1,
854
+				num_posts = num_posts - {int:num_posts}
855
+			WHERE id_board = {int:board}',
856
+			array(
857
+				'board' => $board,
858
+				'num_posts' => $num_replies,
859
+			)
860
+		);
861
+	}
862
+	else
863
+		fatal_lang_error('shd_move_ticket_not_created',false);
864
+
865
+	// Clear our cache
866
+	shd_clear_active_tickets($owner);
867
+
868
+	// Send them to the ticket.
869
+	redirectexit('action=helpdesk;sa=ticket;ticket=' . $ticket);
870
+}
871
+
872
+?>
0 873
\ No newline at end of file
... ...
@@ -0,0 +1,957 @@
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: 1.0 Felidae                             #
17
+# File Info: SimpleDesk.php / 1.0 Felidae                     #
18
+###############################################################
19
+
20
+/**
21
+ *	This file serves as the entry point for SimpleDesk generally, as well as the home of the ticket listing
22
+ *	code, for open, closed and deleted tickets.
23
+ *
24
+ *	@package source
25
+ *	@since 1.0
26
+ */
27
+
28
+if (!defined('SMF'))
29
+	die('Hacking attempt...');
30
+
31
+/**
32
+ *	Begins SimpleDesk general processing.
33
+ *
34
+ *	Several things are done here, the results of which are unilaterally assumed by all other SimpleDesk functions.
35
+ *	<ul>
36
+ *	<li>define standard constants for ticket status and urgency</li>
37
+ *	<li>set up general navigation</li>
38
+ *	<li>see if the URL or POST data contains a ticket, if so sanitise and store that value</li>
39
+ *	<li>see if a msg was specified in the URL, if so identify the relevant ticket</li>
40
+ *	<li>add in the helpdesk CSS file</li>
41
+ *	<li>identify the sub action to direct them to, then send them on their way.</li>
42
+ *	</ul>
43
+ *
44
+ *	@since 1.0
45
+*/
46
+function shd_main()
47
+{
48
+	global $context, $txt, $settings, $sourcedir, $modSettings, $scripturl, $user_profile, $user_info, $smcFunc;
49
+
50
+	// This isn't the SMF way. But for something like this, it's way way more logical and readable.
51
+	define('TICKET_STATUS_NEW', 0);
52
+	define('TICKET_STATUS_PENDING_STAFF', 1);
53
+	define('TICKET_STATUS_PENDING_USER', 2);
54
+	define('TICKET_STATUS_CLOSED', 3);
55
+	define('TICKET_STATUS_WITH_SUPERVISOR', 4);
56
+	define('TICKET_STATUS_ESCALATED', 5);
57
+	define('TICKET_STATUS_DELETED', 6);
58
+
59
+	define('TICKET_URGENCY_LOW', 0);
60
+	define('TICKET_URGENCY_MEDIUM', 1);
61
+	define('TICKET_URGENCY_HIGH', 2);
62
+	define('TICKET_URGENCY_VHIGH', 3);
63
+	define('TICKET_URGENCY_SEVERE', 4);
64
+	define('TICKET_URGENCY_CRITICAL', 5);
65
+
66
+	define('MSG_STATUS_NORMAL', 0);
67
+	define('MSG_STATUS_DELETED', 1);
68
+
69
+	loadTemplate('SimpleDesk');
70
+
71
+	// Basic sanity stuff
72
+	if (!$modSettings['helpdesk_active'])
73
+		fatal_lang_error('shd_inactive', false);
74
+
75
+	shd_is_allowed_to('access_helpdesk');
76
+
77
+	// List of sub actions.
78
+	$subactions = array(
79
+		'main' => array(null, 'shd_main_helpdesk'),
80
+		'viewblock' => array(null, 'shd_view_block'),
81
+		'ticket' => array('SimpleDesk-Display.php', 'shd_view_ticket'),
82
+		'newticket' => array('SimpleDesk-Post.php', 'shd_post_ticket'),
83
+		'editticket' => array('SimpleDesk-Post.php', 'shd_post_ticket'),
84
+		'saveticket' => array('SimpleDesk-Post.php', 'shd_save_post'), // this is the equivalent of post2
85
+		'reply' => array('SimpleDesk-Post.php', 'shd_post_reply'),
86
+		'savereply' => array('SimpleDesk-Post.php', 'shd_save_post'),
87
+		'editreply' => array('SimpleDesk-Post.php', 'shd_post_reply'),
88
+		'markunread' => array('SimpleDesk-MiscActions.php', 'shd_ticket_unread'),
89
+		'assign' => array('SimpleDesk-Assign.php', 'shd_assign'),
90
+		'assign2' => array('SimpleDesk-Assign.php', 'shd_assign2'),
91
+		'resolveticket' => array('SimpleDesk-MiscActions.php', 'shd_ticket_resolve'),
92
+		'ajax' => array('SimpleDesk-AjaxHandler.php', 'shd_ajax'),
93
+		'privacychange' => array('SimpleDesk-MiscActions.php', 'shd_privacy_change_noajax'),
94
+		'urgencychange' => array('SimpleDesk-MiscActions.php', 'shd_urgency_change_noajax'),
95
+		'closedtickets' => array(null, 'shd_closed_tickets'),
96
+		'recyclebin' => array(null, 'shd_recycle_bin'),
97
+		'tickettotopic' => array('SimpleDesk-TicketTopicMove.php', 'shd_tickettotopic'),
98
+		'tickettotopic2' => array('SimpleDesk-TicketTopicMove.php', 'shd_tickettotopic2'),
99
+		'topictoticket' => array('SimpleDesk-TicketTopicMove.php', 'shd_topictoticket'),
100
+		'topictoticket2' => array('SimpleDesk-TicketTopicMove.php', 'shd_topictoticket2'),
101
+		'permadelete' => array('SimpleDesk-Delete.php', 'shd_perma_delete'),
102
+		'deleteticket' => array('SimpleDesk-Delete.php', 'shd_ticket_delete'),
103
+		'deletereply' => array('SimpleDesk-Delete.php', 'shd_reply_delete'),
104
+		'restoreticket' => array('SimpleDesk-Delete.php', 'shd_ticket_restore'),
105
+		'restorereply' => array('SimpleDesk-Delete.php', 'shd_reply_restore'),
106
+	);
107
+
108
+	// What are we doing?
109
+	$_REQUEST['sa'] = (!empty($_REQUEST['sa']) && isset($subactions[$_REQUEST['sa']])) ? $_REQUEST['sa'] : 'main';
110
+	$context['sub_action'] = $subactions[$_REQUEST['sa']];
111
+
112
+	$context['can_new_ticket'] = shd_allowed_to('shd_new_ticket');
113
+	$context['can_view_closed'] = shd_allowed_to('shd_resolve_ticket_own') || shd_allowed_to('shd_resolve_ticket_any');
114
+	$context['can_view_recycle'] = shd_allowed_to('shd_access_recyclebin');
115
+	$context['display_back_to_hd'] = !in_array($_REQUEST['sa'], array('main', 'viewblock', 'recyclebin', 'closedtickets'));
116
+
117
+	// Navigation menu
118
+	$context['navigation'] = array(
119
+		'main' => array(
120
+			'text' => 'shd_home',
121
+			'lang' => true,
122
+			'url' => $scripturl . '?action=helpdesk;sa=main',
123
+		),
124
+		'newticket' => array(
125
+			'text' => 'shd_new_ticket',
126
+			'test' => 'can_new_ticket',
127
+			'lang' => true,
128
+			'url' => $scripturl . '?action=helpdesk;sa=newticket',
129
+		),
130
+		'closedtickets' => array(
131
+			'text' => 'shd_tickets_closed',
132
+			'test' => 'can_view_closed',
133
+			'lang' => true,
134
+			'url' => $scripturl . '?action=helpdesk;sa=closedtickets',
135
+		),
136
+		'recyclebin' => array(
137
+			'text' => 'shd_recycle_bin',
138
+			'test' => 'can_view_recycle',
139
+			'lang' => true,
140
+			'url' => $scripturl . '?action=helpdesk;sa=recyclebin',
141
+		),
142
+		// Only for certain sub areas.
143
+		'back' => array(
144
+			'text' => 'shd_back_to_hd',
145
+			'test' => 'display_back_to_hd',
146
+			'lang' => true,
147
+			'url' => $scripturl . '?action=helpdesk;sa=main',
148
+		),
149
+	);
150
+
151
+	// Highlight the correct button.
152
+	if (isset($context['navigation'][$_REQUEST['sa']]))
153
+		$context['navigation'][$_REQUEST['sa']]['active'] = true;
154
+
155
+	// Build the link tree.
156
+	$context['linktree'][] = array(
157
+		'url' => $scripturl . '?action=helpdesk;sa=main',
158
+		'name' => $txt['shd_helpdesk'],
159
+	);
160
+
161
+	// See if a ticket has been specified, like $topic can be.
162
+	if (!empty($_REQUEST['ticket']))
163
+	{
164
+		if (strpos($_REQUEST['ticket'], '.') === false)
165
+		{
166
+			$context['ticket_id'] = (int) $_REQUEST['ticket'];
167
+			$context['ticket_start'] = 0;
168
+		}
169
+		else
170
+		{
171
+			list ($context['ticket_id'], $context['ticket_start']) = explode('.', $_REQUEST['ticket']);
172
+			$context['ticket_id'] = (int) $context['ticket_id'];
173
+			if (!is_numeric($context['ticket_start']))
174
+			{
175
+				if (substr($context['ticket_start'], 0, 3) == 'msg')
176
+				{
177
+					$virtual_msg = (int) substr($context['ticket_start'], 3);
178
+					$query = shd_db_query('', '
179
+						SELECT COUNT(hdtr.id_msg)
180
+						FROM {db_prefix}helpdesk_ticket_replies AS hdtr
181
+							INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hdtr.id_ticket = hdt.id_ticket)
182
+						WHERE {query_see_ticket}
183
+							AND hdtr.id_ticket = {int:ticket}
184
+							AND hdtr.id_msg > hdt.id_first_msg
185
+							AND hdtr.id_msg < {int:virtual_msg}',
186
+						array(
187
+							'ticket' => $context['ticket_id'],
188
+							'virtual_msg' => $virtual_msg,
189
+						)
190
+					);
191
+					list ($context['ticket_start']) = $smcFunc['db_fetch_row']($query);
192
+					$smcFunc['db_free_result']($query);
193
+				}
194
+			}
195
+			else
196
+				$context['ticket_start'] = (int) $context['ticket_start']; // it IS numeric but let's make sure it's the right kind of number
197
+		}
198
+	}
199
+
200
+	// Do we have just a message id? We can get the ticket from that - but only if we don't already have a ticket id!
201
+	$_REQUEST['msg'] = !empty($_REQUEST['msg']) ? (int) $_REQUEST['msg'] : 0;
202
+	if (!empty($_REQUEST['msg']) && empty($context['ticket_id']))
203
+	{
204
+		$query = shd_db_query('', '
205
+			SELECT hdt.id_ticket, hdtr.id_msg
206
+			FROM {db_prefix}helpdesk_ticket_replies AS hdtr
207
+				INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hdtr.id_ticket = hdt.id_ticket)
208
+			WHERE {query_see_ticket}
209
+				AND hdtr.id_msg = {int:msg}',
210
+			array(
211
+				'msg' => $_REQUEST['msg'],
212
+			)
213
+		);
214
+
215
+		if ($row = $smcFunc['db_fetch_row']($query))
216
+			$context['ticket_id'] = (int) $row[0];
217
+
218
+		$smcFunc['db_free_result']($query);
219
+	}
220
+
221
+	$context['items_per_page'] = 10;
222
+	$context['start'] = isset($_REQUEST['start']) ? $_REQUEST['start'] : 0;
223
+
224
+	// Load the custom CSS.
225
+	if (empty($context['html_headers']))
226
+		$context['html_headers'] = '';
227
+
228
+	$context['html_headers'] .= '
229
+	<link rel="stylesheet" type="text/css" href="' . (file_exists($settings['theme_dir'] . '/css/helpdesk.css') ? $settings['theme_url'] . '/css/helpdesk.css' : $settings['default_theme_url'] . '/css/helpdesk.css') . '" />
230
+	<script type="text/javascript" src="' . $settings['default_theme_url'] . '/scripts/helpdesk.js?rc2"></script>';
231
+
232
+	// Darn IE6. Die, already :(
233
+	if ($context['browser']['is_ie6'])
234
+		$context['html_headers'] .= '
235
+		<!-- Fall back, dark force, for we shall thou evil powers not endorse -->
236
+		<link rel="stylesheet" type="text/css" href="' . $settings['default_theme_url'] . '/css/helpdesk_ie6.css" />';
237
+
238
+	// Send them away.
239
+	if ($context['sub_action'][0] !== null)
240
+		require ($sourcedir . '/' . $context['sub_action'][0]);
241
+
242
+	$context['sub_action'][1]();
243
+}
244
+
245
+/**
246
+ *	Display the main front page, showing tickets waiting for staff, waiting for user feedback and so on.
247
+ *
248
+ *	This function sets up multiple blocks to be shown to users, defines what columns these blocks should have and states
249
+ *	the rules to be used in getting the data.
250
+ *
251
+ *	Each block has multiple parameters, and is stated in $context['ticket_blocks']:
252
+ *	<ul>
253
+ *	<li>block_icon: which image to use in Themes/default/images/simpledesk for denoting the type of block; filename plus extension</li>
254
+ *	<li>title: the text string to use as the block's heading</li>
255
+ *	<li>where: an SQL clause denoting the rule for obtaining the items in this block</li>
256
+ *	<li>display: whether the block should be processed and prepared for display</li>
257
+ *	<li>count: the number of items in this block, for pagination; generally should be a call to {@link shd_count_helpdesk_tickets()}</li>
258
+ *	<li>columns: an array of columns to display in this block, in the order they should be displayed, using the following options, derived from {@link shd_get_block_columns()}:
259
+ *		<ul>
260
+ *			<li>ticket_id: the ticket's read status, privacy icon, and id</li>
261
+ *			<li>ticket_name: name/link to the ticket</li>
262
+ *			<li>starting_user: profile link to the user who opened the ticket</li>
263
+ *			<li>replies: number of (visible) replies in the ticket</li>
264
+ *			<li>allreplies: number of (all) replies in the ticket (includes deleted replies, which 'replies' does not)</li>
265
+ *			<li>last_reply: the user who last replied</li>
266
+ *			<li>status: the current ticket's status</li>
267
+ *			<li>assigned: link to the profile of the user the ticket is assigned to, or 'Unassigned' if not assigned</li>
268
+ *			<li>urgency: the current ticket's urgency</li>
269
+ *			<li>updated: time of the last reply in the ticket; states Never if no replies</li>
270
+ *			<li>actions: icons that may or may not relate to a given ticket; buttons for recycle, delete, unresolve live in this column</li>
271
+ *		</ul>
272
+ *	<li>required: whether the block is required to be displayed even if empty</li>
273
+ *	<li>collapsed: whether the block should be compressed to a header with count of tickets or not (mostly for {@link shd_view_block()}'s benefit)</li>
274
+ *	</ul>
275
+ *
276
+ *	This function declares the following blocks:
277
+ *	<ul>
278
+ *	<li>Assigned to me (staff only)</li>
279
+ *	<li>New tickets (staff only)</li>
280
+ *	<li>Pending with staff (for staff, this is just tickets with that status, for regular users this is both pending staff and new unreplied to tickets)</li>
281
+ *	<li>Pending with user (both)</li>
282
+ *	</ul>
283
+ *
284
+ *	@see shd_count_helpdesk_tickets()
285
+ *	@since 1.0
286
+*/
287
+function shd_main_helpdesk()
288
+{
289
+	global $context, $txt, $smcFunc, $user_profile, $scripturl, $user_info;
290
+
291
+	$is_staff = shd_allowed_to('shd_staff');
292
+	// Stuff we need to add to $context, page title etc etc
293
+	$context += array(
294
+		'page_title' => $txt['shd_helpdesk'],
295
+		'sub_template' => 'main',
296
+		'ticket_blocks' => array( // the numbers tie back to the master status idents
297
+			'assigned' => array(
298
+				'block_icon' => 'assign.png',
299
+				'title' => $txt['shd_status_assigned_heading'],
300
+				'where' => 'hdt.id_member_assigned = ' . $user_info['id'] . ' AND hdt.status NOT IN (' . TICKET_STATUS_CLOSED . ',' . TICKET_STATUS_DELETED . ')',
301
+				'display' => $is_staff,
302
+				'count' => shd_count_helpdesk_tickets('assigned'),
303
+				'columns' => shd_get_block_columns('assigned'),
304
+				'required' => $is_staff,
305
+				'collapsed' => false,
306
+			),
307
+			'new' => array(
308
+				'block_icon' => 'status.png',
309
+				'title' => $txt['shd_status_' . TICKET_STATUS_NEW . '_heading'],
310
+				'where' => 'hdt.id_member_assigned != ' . $user_info['id'] . ' AND hdt.status = ' . TICKET_STATUS_NEW,
311
+				'display' => $is_staff,
312
+				'count' => shd_count_helpdesk_tickets('new'),
313
+				'columns' => shd_get_block_columns('new'),
314
+				'required' => false,
315
+				'collapsed' => false,
316
+			),
317
+			'staff' => array(
318
+				'block_icon' => 'staff.png',
319
+				'title' => $txt['shd_status_' . TICKET_STATUS_PENDING_STAFF . '_heading'],
320
+				'where' => $is_staff ? ('hdt.id_member_assigned != ' . $user_info['id'] . ' AND hdt.status = ' . TICKET_STATUS_PENDING_STAFF) : ('hdt.status IN (' . TICKET_STATUS_NEW . ',' . TICKET_STATUS_PENDING_STAFF . ')'), // put new and with staff together in 'waiting for staff' for end user
321
+				'display' => true,
322
+				'count' => shd_count_helpdesk_tickets('staff', $is_staff),
323
+				'columns' => shd_get_block_columns('staff'),
324
+				'required' => true,
325
+				'collapsed' => false,
326
+			),
327
+			'user' => array(
328
+				'block_icon' => 'user.png',
329
+				'title' => $txt['shd_status_' . TICKET_STATUS_PENDING_USER . '_heading'],
330
+				'where' => $is_staff ? ('hdt.id_member_assigned != ' . $user_info['id'] . ' AND hdt.status = ' . TICKET_STATUS_PENDING_USER) : ('hdt.status = ' . TICKET_STATUS_PENDING_USER),
331
+				'display' => true,
332
+				'count' => shd_count_helpdesk_tickets('with_user'),
333
+				'columns' => shd_get_block_columns($is_staff ? 'user_staff' : 'user_user'),
334
+				'required' => true,
335
+				'collapsed' => false,
336
+			),
337
+		),
338
+		'shd_home_view' => shd_allowed_to('shd_staff') ? 'staff' : 'user',
339
+	);
340
+
341
+	shd_helpdesk_listing();
342
+}
343
+
344
+/**
345
+ *	Sets up viewing of a single block without any pagination.
346
+ *
347
+ *	This provides the ability to see all of a given type of ticket at once without paging through them, which are all sortable.
348
+ *
349
+ *	@see shd_main_helpdesk()
350
+ *	@since 1.0
351
+*/
352
+function shd_view_block()
353
+{
354
+	global $context, $txt, $smcFunc, $user_profile, $scripturl, $user_info;
355
+
356
+	$is_staff = shd_allowed_to('shd_staff');
357
+	// Stuff we need to add to $context, page title etc etc
358
+	$context += array(
359
+		'page_title' => $txt['shd_helpdesk'],
360
+		'sub_template' => 'main',
361
+		'ticket_blocks' => array( // the numbers tie back to the master status idents
362
+			'assigned' => array(
363
+				'block_icon' => 'assign.png',
364
+				'title' => $txt['shd_status_assigned_heading'],
365
+				'where' => 'hdt.id_member_assigned = ' . $user_info['id'] . ' AND hdt.status NOT IN (' . TICKET_STATUS_CLOSED . ',' . TICKET_STATUS_DELETED . ')',
366
+				'display' => $is_staff,
367
+				'count' => shd_count_helpdesk_tickets('assigned'),
368
+				'columns' => shd_get_block_columns('assigned'),
369
+				'required' => $is_staff,
370
+				'collapsed' => false,
371
+			),
372
+			'new' => array(
373
+				'block_icon' => 'status.png',
374
+				'title' => $txt['shd_status_' . TICKET_STATUS_NEW . '_heading'],
375
+				'where' => 'hdt.id_member_assigned != ' . $user_info['id'] . ' AND hdt.status = ' . TICKET_STATUS_NEW,
376
+				'display' => $is_staff,
377
+				'count' => shd_count_helpdesk_tickets('new'),
378
+				'columns' => shd_get_block_columns('new'),
379
+				'required' => false,
380
+				'collapsed' => false,
381
+			),
382
+			'staff' => array(
383
+				'block_icon' => 'staff.png',
384
+				'title' => $txt['shd_status_' . TICKET_STATUS_PENDING_STAFF . '_heading'],
385
+				'where' => $is_staff ? ('hdt.id_member_assigned != ' . $user_info['id'] . ' AND hdt.status = ' . TICKET_STATUS_PENDING_STAFF) : ('hdt.status IN (' . TICKET_STATUS_NEW . ',' . TICKET_STATUS_PENDING_STAFF . ')'), // put new and with staff together in 'waiting for staff' for end user
386
+				'display' => true,
387
+				'count' => shd_count_helpdesk_tickets('staff', $is_staff),
388
+				'columns' => shd_get_block_columns('staff'),
389
+				'required' => true,
390
+				'collapsed' => false,
391
+			),
392
+			'user' => array(
393
+				'block_icon' => 'user.png',
394
+				'title' => $txt['shd_status_' . TICKET_STATUS_PENDING_USER . '_heading'],
395
+				'where' => $is_staff ? ('hdt.id_member_assigned != ' . $user_info['id'] . ' AND hdt.status = ' . TICKET_STATUS_PENDING_USER) : ('hdt.status = ' . TICKET_STATUS_PENDING_USER),
396
+				'display' => true,
397
+				'count' => shd_count_helpdesk_tickets('with_user'),
398
+				'columns' => shd_get_block_columns($is_staff ? 'user_staff' : 'user_user'),
399
+				'required' => true,
400
+				'collapsed' => false,
401
+			),
402
+		),
403
+		'shd_home_view' => shd_allowed_to('shd_staff') ? 'staff' : 'user',
404
+	);
405
+
406
+	if (empty($_REQUEST['block']) || empty($context['ticket_blocks'][$_REQUEST['block']]) || empty($context['ticket_blocks'][$_REQUEST['block']]['count']))
407
+		redirectexit('action=helpdesk;sa=main');
408
+
409
+	$context['items_per_page'] = 10;
410
+	foreach ($context['ticket_blocks'] as $block => $details)
411
+	{
412
+		if ($block == $_REQUEST['block'])
413
+			$context['items_per_page'] = $details['count'];
414
+		else
415
+			$context['ticket_blocks'][$block]['collapsed'] = true;
416
+	}
417
+
418
+	shd_helpdesk_listing();
419
+}
420
+
421
+/**
422
+ *	Set up the paginated lists of closed tickets.
423
+ *
424
+ *	Much like the main helpdesk, this function prepares a list of all the closed/resolved tickets, with a more specific
425
+ *	list of columns that is better suited to resolved tickets.
426
+ *
427
+ *	@see shd_main_helpdesk()
428
+ *	@since 1.0
429
+*/
430
+function shd_closed_tickets()
431
+{
432
+	global $context, $txt, $smcFunc, $user_profile, $scripturl, $settings, $user_info;
433
+
434
+	if (!shd_allowed_to('shd_resolve_ticket_own') && shd_allowed_to('shd_resolve_ticket_any'))
435
+		fatal_lang_error('shd_cannot_view_resolved', false);
436
+
437
+	// Stuff we need to add to $context, the permission we want to use, page title etc etc
438
+	$context += array(
439
+		'page_title' => $txt['shd_helpdesk'],
440
+		'sub_template' => 'closedtickets',
441
+		'ticket_blocks' => array(
442
+			'closed' => array(
443
+				'block_icon' => 'resolved.png',
444
+				'title' => $txt['shd_status_' . TICKET_STATUS_CLOSED . '_heading'],
445
+				'where' => 'hdt.status = ' . TICKET_STATUS_CLOSED,
446
+				'display' => true,
447
+				'count' => shd_count_helpdesk_tickets('closed'),
448
+				'columns' => shd_get_block_columns('closed'),
449
+				'required' => true,
450
+				'collapsed' => false,
451
+			),
452
+		),
453
+		'shd_home_view' => shd_allowed_to('shd_staff') ? 'staff' : 'user', // This might be removed in the future. We do this here to be able to re-use template_ticket_block() in the template.
454
+	);
455
+
456
+	// Build the link tree.
457
+	$context['linktree'][] = array(
458
+		'url' => $scripturl . '?action=helpdesk;sa=main',
459
+		'name' => $txt['shd_linktree_tickets'],
460
+	);
461
+	$context['linktree'][] = array(
462
+		'url' => $scripturl . '?action=helpdesk;sa=closedtickets',
463
+		'name' => $txt['shd_tickets_closed'],
464
+	);
465
+
466
+	shd_helpdesk_listing();
467
+}
468
+
469
+/**
470
+ *	Set up the paginated lists of deleted/recyclebin tickets.
471
+ *
472
+ *	Much like the main helpdesk, this function prepares a list of all the deleted tickets, with a more specific
473
+ *	list of columns that is better suited to recyclable or permadeletable tickets.
474
+ *
475
+ *	@see shd_main_helpdesk()
476
+ *	@since 1.0
477
+*/
478
+function shd_recycle_bin()
479
+{
480
+	global $context, $txt, $smcFunc, $user_profile, $scripturl, $settings, $user_info;
481
+
482
+	// Stuff we need to add to $context, the permission we want to use, page title etc etc
483
+	$context += array(
484
+		'shd_permission' => 'shd_access_recyclebin',
485
+		'page_title' => $txt['shd_helpdesk'],
486
+		'sub_template' => 'recyclebin',
487
+		'ticket_blocks' => array(
488
+			'recycle' => array(
489
+				'block_icon' => 'recycle.png',
490
+				'title' => $txt['shd_status_' . TICKET_STATUS_DELETED . '_heading'],
491
+				'tickets' => array(),
492
+				'where' => 'hdt.status = ' . TICKET_STATUS_DELETED,
493
+				'display' => true,
494
+				'count' => shd_count_helpdesk_tickets('recycled'),
495
+				'columns' => shd_get_block_columns('recycled'),
496
+				'required' => true,
497
+				'collapsed' => false,
498
+			),
499
+			'withdeleted' => array(
500
+				'block_icon' => 'recycle.png',
501
+				'title' => $txt['shd_status_withdeleted_heading'],
502
+				'tickets' => array(),
503
+				'where' => 'hdt.status != ' . TICKET_STATUS_DELETED . ' AND hdt.deleted_replies > 0',
504
+				'display' => true,
505
+				'count' => shd_count_helpdesk_tickets('withdeleted'),
506
+				'columns' => shd_get_block_columns('withdeleted'),
507
+				'required' => true,
508
+				'collapsed' => false,
509
+			),
510
+		),
511
+	);
512
+
513
+	// Build the link tree.
514
+	$context['linktree'][] = array(
515
+		'url' => $scripturl . '?action=helpdesk;sa=recyclebin',
516
+		'name' => $txt['shd_recycle_bin'],
517
+	);
518
+
519
+	shd_helpdesk_listing();
520
+}
521
+
522
+/**
523
+ *	Gather the data and prepare to display the ticket blocks.
524
+ *
525
+ *	Actually performs the queries to get data for each block, subject to the parameters specified by the calling functions.
526
+ *
527
+ *	It also sets up per-block pagination links, collects a variety of data (enough to populate all the columns as listed in shd_main_helpdesk,
528
+ *	even if not entirely applicable, and populates it all into $context['ticket_blocks']['tickets'], extending the array that was
529
+ *	already there.
530
+ *
531
+ *	@see shd_main_helpdesk()
532
+ *	@see shd_closed_tickets()
533
+ *	@see shd_recycle_bin()
534
+ *	@since 1.0
535
+*/
536
+function shd_helpdesk_listing()
537
+{
538
+	global $context, $txt, $smcFunc, $user_profile, $scripturl, $settings, $user_info, $modSettings;
539
+
540
+	if (!empty($context['shd_permission']))
541
+		shd_is_allowed_to($context['shd_permission']);
542
+
543
+	$num_per_page = $context['items_per_page'];
544
+	$block_list = array_keys($context['ticket_blocks']);
545
+	$primary_url = '?action=helpdesk;sa=' . $_REQUEST['sa'];
546
+
547
+	// First figure out the start positions of each item and sanitise them
548
+	foreach ($context['ticket_blocks'] as $block_key => $block)
549
+	{
550
+		$start = empty($_REQUEST['st_' . $block_key]) ? 0 : (int) $_REQUEST['st_' . $block_key];
551
+		$max_value = $block['count']; // easier to read
552
+
553
+		if ($start < 0)
554
+			$start = 0;
555
+		elseif ($start >= $max_value)
556
+			$start = max(0, (int) $max_value - (((int) $max_value % (int) $num_per_page) == 0 ? $num_per_page : ((int) $max_value % (int) $num_per_page)));
557
+		else
558
+			$start = max(0, (int) $start - ((int) $start % (int) $num_per_page));
559
+
560
+		$context['ticket_blocks'][$block_key]['start'] = $start;
561
+		if ($start != 0)
562
+			$_REQUEST['st_' . $block_key] = $start; // sanitise!
563
+		elseif (isset($_REQUEST['st_' . $block_key]))
564
+			unset($_REQUEST['st_' . $block_key]);
565
+	}
566
+
567
+	// Now ordering the columns, separate loop for breaking the two processes apart
568
+	$sort_methods = array(
569
+		'ticketid' => array(
570
+			'sql' => 'hdt.id_ticket',
571
+		),
572
+		'ticketname' => array(
573
+			'sql' => 'hdt.subject',
574
+		),
575
+		'replies' => array(
576
+			'sql' => 'hdt.num_replies',
577
+		),
578
+		'allreplies' => array(
579
+			'sql' => '(hdt.num_replies + hdt.deleted_replies)',
580
+		),
581
+		'urgency' => array(
582
+			'sql' => 'hdt.urgency',
583
+		),
584
+		'updated' => array(
585
+			'sql' => 'hdt.id_last_msg',
586
+		),
587
+		'assigned' => array(
588
+			'sql' => 'assigned_name',
589
+			'sql_select' => 'IFNULL(mem.real_name, 0) AS assigned_name',
590
+			'sql_join' => 'LEFT JOIN {db_prefix}members AS mem ON (hdt.id_member_assigned = mem.id_member)',
591
+		),
592
+		'status' => array(
593
+			'sql' => 'hdt.status',
594
+		),
595
+		'starter' => array(
596
+			'sql' => 'starter_name',
597
+			'sql_select' => 'IFNULL(mem.real_name, 0) AS starter_name',
598
+			'sql_join' => 'LEFT JOIN {db_prefix}members AS mem ON (hdt.id_member_started = mem.id_member)',
599
+		),
600
+		'lastreply' => array(
601
+			'sql' => 'last_reply',
602
+			'sql_select' => 'IFNULL(mem.real_name, 0) AS last_reply',
603
+			'sql_join' => 'LEFT JOIN {db_prefix}members AS mem ON (hdtr_last.id_member = mem.id_member)',
604
+		),
605
+	);
606
+
607
+	foreach ($context['ticket_blocks'] as $block_key => $block)
608
+	{
609
+		$sort = isset($_REQUEST['so_' . $block_key]) ? $_REQUEST['so_' . $block_key] : '';
610
+
611
+		if (strpos($sort, '_') > 0 && substr_count($sort, '_') == 1)
612
+		{
613
+			list($sort_item, $sort_dir) = explode('_', $sort);
614
+
615
+			if (empty($sort_methods[$sort_item]))
616
+			{
617
+				$sort_item = 'updated';
618
+				$sort = '';
619
+			}
620
+
621
+			if (!in_array($sort_dir, array('asc', 'desc')))
622
+			{
623
+				$sort = '';
624
+				$sort_dir = 'asc';
625
+			}
626
+		}
627
+		else
628
+		{
629
+			$sort = '';
630
+			$sort_item = 'updated';
631
+			$sort_dir = $_REQUEST['sa'] == 'closedtickets' || $_REQUEST['sa'] == 'recyclebin' ? 'desc' : 'asc'; // default to newest first if on recyclebin or closed tickets, otherwise oldest first
632
+		}
633
+
634
+		if ($sort != '')
635
+			$_REQUEST['so_' . $block_key] = $sort; // sanitise!
636
+		elseif (isset($_REQUEST['so_' . $block_key]))
637
+			unset($_REQUEST['so_' . $block_key]);
638
+
639
+		$context['ticket_blocks'][$block_key]['sort'] = array(
640
+			'item' => $sort_item,
641
+			'direction' => $sort_dir,
642
+			'add_link' => ($sort != ''),
643
+			'sql' => array(
644
+				'select' => !empty($sort_methods[$sort_item]['sql_select']) ? $sort_methods[$sort_item]['sql_select'] : '',
645
+				'join' => !empty($sort_methods[$sort_item]['sql_join']) ? $sort_methods[$sort_item]['sql_join'] : '',
646
+				'sort' => $sort_methods[$sort_item]['sql'] . ' ' . strtoupper($sort_dir),
647
+			),
648
+			'link_bits' => array(),
649
+		);
650
+	}
651
+
652
+	// Having got all that, step through the blocks again to determine the full URL fragments
653
+	foreach ($context['ticket_blocks'] as $block_key => $block)
654
+		foreach ($sort_methods as $method => $sort_details)
655
+			$context['ticket_blocks'][$block_key]['sort']['link_bits'][$method] = ';so_' . $block_key . '=' . $method . '_' . $block['sort']['direction'];
656
+
657
+	// Now go actually do the whole block thang, setting up space for a list of users as we go along
658
+	$users = array();
659
+
660
+	foreach ($context['ticket_blocks'] as $block_key => $block)
661
+	{
662
+		if (empty($block['display']) || !empty($block['collapsed']))
663
+			continue;
664
+
665
+		$context['ticket_blocks'][$block_key]['tickets'] = array();
666
+
667
+		$query = shd_db_query('', '
668
+			SELECT hdt.id_ticket, hdt.id_last_msg, hdt.id_member_started, hdt.id_member_updated, hdt.id_member_assigned,
669
+				hdt.subject, hdt.status, hdt.num_replies, hdt.deleted_replies, hdt.private, hdt.urgency,
670
+				hdtr_first.poster_name AS ticket_opener, hdtr_last.poster_name AS respondent, hdtr_last.poster_time,
671
+				IFNULL(hdlr.id_msg, 0) AS log_read' . (!empty($block['sort']['sql']['select']) ? ', ' . $block['sort']['sql']['select'] : '') . '
672
+			FROM {db_prefix}helpdesk_tickets AS hdt
673
+				INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr_first ON (hdt.id_first_msg = hdtr_first.id_msg)
674
+				INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr_last ON (hdt.id_last_msg = hdtr_last.id_msg)
675
+				LEFT JOIN {db_prefix}helpdesk_log_read AS hdlr ON (hdt.id_ticket = hdlr.id_ticket AND hdlr.id_member = {int:user})
676
+				' . (!empty($block['sort']['sql']['join']) ? $block['sort']['sql']['join'] : '') . '
677
+			WHERE {query_see_ticket}' . (!empty($block['where']) ? ' AND ' . $block['where'] : '') . '
678
+			ORDER BY ' . (!empty($block['sort']['sql']['sort']) ? $block['sort']['sql']['sort'] : 'hdt.id_last_msg ASC') . '
679
+			LIMIT {int:start}, {int:items_per_page}',
680
+			array(
681
+				'user' => $context['user']['id'],
682
+				'start' => $block['start'],
683
+				'items_per_page' => $context['items_per_page'],
684
+			)
685
+		);
686
+
687
+		while ($row = $smcFunc['db_fetch_assoc']($query))
688
+		{
689
+			$is_own = $user_info['id'] == $row['id_member_started'];
690
+			censorText($row['subject']);
691
+
692
+			$new_block = array(
693
+				'id' => $row['id_ticket'],
694
+				'display_id' => str_pad($row['id_ticket'], 5, '0', STR_PAD_LEFT),
695
+				'link' => '<a href="' . $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $row['id_ticket'] . ($_REQUEST['sa'] == 'recyclebin' ? ';recycle' : '') . '">' . $row['subject'] . '</a>',
696
+				'subject' => $row['subject'],
697
+				'status' => array(
698
+					'level' => $row['status'],
699
+					'label' => $txt['shd_status_' . $row['status']],
700
+				),
701
+				'starter' => array(
702
+					'id' => $row['id_member_started'],
703
+					'name' => $row['ticket_opener'],
704
+				),
705
+				'last_update' => $row['status'] != TICKET_STATUS_NEW ? timeformat($row['poster_time']) : $txt['never'],
706
+				'assigned' => array(
707
+					'id' => $row['id_member_assigned'],
708
+				),
709
+				'respondent' => array(
710
+					'id' => $row['id_member_updated'],
711
+					'name' => $row['respondent'],
712
+				),
713
+				'urgency' => array(
714
+					'level' => $row['urgency'],
715
+					'label' => $row['urgency'] > TICKET_URGENCY_HIGH ? '<span class="error">' . $txt['shd_urgency_' . $row['urgency']] . '</span>' : $txt['shd_urgency_' . $row['urgency']],
716
+				),
717
+				'is_unread' => ($row['id_last_msg'] > $row['log_read']),
718
+				'private' => $row['private'],
719
+				'actions' => array(),
720
+				'num_replies' => $row['num_replies'],
721
+				'all_replies' => (int) $row['num_replies'] + (int) $row['deleted_replies'],
722
+			);
723
+
724
+			if ($row['status'] == TICKET_STATUS_CLOSED)
725
+			{
726
+				$new_block['actions'] += array(
727
+					'resolve' => shd_allowed_to('shd_resolve_ticket_any') || ($is_own && shd_allowed_to('shd_resolve_ticket_own')) ? '<a href="' . $scripturl . '?action=helpdesk;sa=resolveticket;ticket=' . $row['id_ticket'] . ';home;' . $context['session_var'] . '=' . $context['session_id'] . '" title="' . $txt['shd_ticket_unresolved'] . '"><img src="' . $settings['default_images_url'] . '/simpledesk/unresolved.png" alt="' . $txt['shd_ticket_unresolved'] . '" /></a>' : '',
728
+				);
729
+			}
730
+			elseif ($row['status'] == TICKET_STATUS_DELETED) // and thus, we're in the recycle bin
731
+			{
732
+				$new_block['actions'] += array(
733
+					'restore' => shd_allowed_to('shd_restore_ticket_any') || ($is_own && shd_allowed_to('shd_restore_ticket_own')) ? '<a href="' . $scripturl . '?action=helpdesk;sa=restoreticket;ticket=' . $row['id_ticket'] . ';' . $context['session_var'] . '=' . $context['session_id'] . '" title="' . $txt['shd_ticket_restore'] . '"><img src="' . $settings['default_images_url'] . '/simpledesk/restore.png" alt="' . $txt['shd_ticket_restore'] . '" /></a>' : '',
734
+					'permadelete' => shd_allowed_to('shd_delete_recycling') ? '<a href="' . $scripturl . '?action=helpdesk;sa=permadelete;ticket=' . $row['id_ticket'] . ';' . $context['session_var'] . '=' . $context['session_id'] . '" title="' . $txt['shd_delete_permanently'] . '" onclick="return confirm(' . JavaScriptEscape($txt['shd_delete_permanently_confirm']) . ');"><img src="' . $settings['default_images_url'] . '/simpledesk/delete.png" alt="' . $txt['shd_delete_permanently'] . '" /></a>' : '',
735
+				);
736
+			}
737
+			else
738
+			{
739
+				$langstring = '';
740
+				if (shd_allowed_to('shd_assign_ticket_any'))
741
+					$langstring = empty($row['id_member_assigned']) ? $txt['shd_ticket_assign'] : $txt['shd_ticket_reassign'];
742
+				elseif (shd_allowed_to('shd_assign_ticket_own') && (empty($row['id_member_assigned']) || $row['id_member_assigned'] == $context['user']['id']))
743
+					$langstring = $row['id_member_assigned'] == $context['user']['id'] ? $txt['shd_ticket_unassign'] : $txt['shd_ticket_assign_self'];
744
+
745
+				if (!empty($langstring))
746
+					$new_block['actions']['assign'] = '<a href="' . $scripturl . '?action=helpdesk;sa=assign;ticket=' . $row['id_ticket'] . ';home;' . $context['session_var'] . '=' . $context['session_id'] . '" title="' . $langstring . '"><img src="' . $settings['default_images_url'] . '/simpledesk/assign.png" alt="' . $langstring . '" /></a>';
747
+
748
+				$new_block['actions'] += array(
749
+					'resolve' => shd_allowed_to('shd_resolve_ticket_any') || ($is_own && shd_allowed_to('shd_resolve_ticket_own')) ? '<a href="' . $scripturl . '?action=helpdesk;sa=resolveticket;ticket=' . $row['id_ticket'] . ';home;' . $context['session_var'] . '=' . $context['session_id'] . '" title="' . $txt['shd_ticket_resolved'] . '"><img src="' . $settings['default_images_url'] . '/simpledesk/resolved.png" alt="' . $txt['shd_ticket_resolved'] . '" /></a>' : '',
750
+					'tickettotopic' => empty($modSettings['shd_helpdesk_only']) && shd_allowed_to('shd_ticket_to_topic') && ($row['deleted_replies'] == 0 || shd_allowed_to('shd_access_recyclebin')) ? '<a href="' . $scripturl . '?action=helpdesk;sa=tickettotopic;ticket=' . $row['id_ticket'] . ';' . $context['session_var'] . '=' . $context['session_id'] . '" title="' . $txt['shd_ticket_move_to_topic'] . '"><img src="' . $settings['default_images_url'] . '/simpledesk/tickettotopic.png" alt="' . $txt['shd_ticket_move_to_topic'] . '" /></a>' : '',
751
+					'delete' => shd_allowed_to('shd_delete_ticket_any') || ($is_own && shd_allowed_to('shd_delete_ticket_own')) ? '<a href="' . $scripturl . '?action=helpdesk;sa=deleteticket;ticket=' . $row['id_ticket'] . ';' . $context['session_var'] . '=' . $context['session_id'] . '" title="' . $txt['shd_ticket_delete'] . '" onclick="return confirm(' . JavaScriptEscape($txt['shd_delete_confirm']) . ');"><img src="' . $settings['default_images_url'] . '/simpledesk/delete.png" alt="' . $txt['shd_ticket_delete'] . '" /></a>' : '',
752
+				);
753
+			}
754
+
755
+			$context['ticket_blocks'][$block_key]['tickets'][] = $new_block;
756
+
757
+			$users[] = $row['id_member_started'];
758
+			$users[] = $row['id_member_updated'];
759
+			$users[] = $row['id_member_assigned'];
760
+		}
761
+		$smcFunc['db_free_result']($query);
762
+	}
763
+
764
+	$users = array_unique($users);
765
+	if (!empty($users))
766
+		loadMemberData($users, false, 'minimal');
767
+
768
+	foreach ($context['ticket_blocks'] as $block_id => $block)
769
+	{
770
+		if (empty($block['tickets']))
771
+			continue;
772
+
773
+		foreach ($block['tickets'] as $tid => $ticket)
774
+		{
775
+			// Set up names and profile links for topic starter
776
+			if (!empty($user_profile[$ticket['starter']['id']]))
777
+			{
778
+				// We found the name, so let's use their current name and profile link
779
+				$context['ticket_blocks'][$block_id]['tickets'][$tid]['starter']['name'] = $user_profile[$ticket['starter']['id']]['real_name'];
780
+				$context['ticket_blocks'][$block_id]['tickets'][$tid]['starter']['link'] = shd_profile_link($user_profile[$ticket['starter']['id']]['real_name'], $ticket['starter']['id']);
781
+			}
782
+			else
783
+				// We didn't, so keep using the name we found previously and don't make an actual link
784
+				$context['ticket_blocks'][$block_id]['tickets'][$tid]['starter']['link'] = $context['ticket_blocks'][$block_id]['tickets'][$tid]['starter']['name'];
785
+
786
+			// Set up names and profile links for assigned user
787
+			if ($ticket['assigned']['id'] == 0 || empty($user_profile[$ticket['assigned']['id']]))
788
+			{
789
+				$context['ticket_blocks'][$block_id]['tickets'][$tid]['assigned']['name'] = $txt['shd_unassigned'];
790
+				$context['ticket_blocks'][$block_id]['tickets'][$tid]['assigned']['link'] = '<span class="error">' . $txt['shd_unassigned'] . '</span>';
791
+			}
792
+			else
793
+			{
794
+				$context['ticket_blocks'][$block_id]['tickets'][$tid]['assigned']['name'] = $user_profile[$ticket['assigned']['id']]['real_name'];
795
+				$context['ticket_blocks'][$block_id]['tickets'][$tid]['assigned']['link'] = shd_profile_link($user_profile[$ticket['assigned']['id']]['real_name'], $ticket['assigned']['id']);
796
+			}
797
+
798
+			// And last respondent
799
+			if ($ticket['respondent']['id'] == 0 || empty($user_profile[$ticket['respondent']['id']]))
800
+			{
801
+				// Didn't find the name, so reuse what we have
802
+				$context['ticket_blocks'][$block_id]['tickets'][$tid]['respondent']['link'] = $context['ticket_blocks'][$block_id]['tickets'][$tid]['respondent']['name'];
803
+			}
804
+			else
805
+			{
806
+				$context['ticket_blocks'][$block_id]['tickets'][$tid]['respondent']['name'] = $user_profile[$ticket['respondent']['id']]['real_name'];
807
+				$context['ticket_blocks'][$block_id]['tickets'][$tid]['respondent']['link'] = shd_profile_link($user_profile[$ticket['respondent']['id']]['real_name'], $ticket['respondent']['id']);
808
+			}
809
+		}
810
+	}
811
+
812
+	foreach ($context['ticket_blocks'] as $block_id => $block)
813
+	{
814
+		if (empty($block['display']) || (empty($block['count']) && !$block['required'] && empty($block['collapsed'])))
815
+			unset($context['ticket_blocks'][$block_id]);
816
+	}
817
+
818
+	$base_url = '';
819
+	foreach ($context['ticket_blocks'] as $block_id => $block)
820
+	{
821
+		if ($block['sort']['add_link'])
822
+			$base_url .= $block['sort']['link_bits'][$block['sort']['item']];
823
+	}
824
+
825
+	if ($_REQUEST['sa'] != 'viewblock')
826
+	{
827
+		foreach ($context['ticket_blocks'] as $block_id => $block)
828
+		{
829
+			$url_fragment = $base_url;
830
+
831
+			foreach ($block_list as $block_item)
832
+			{
833
+				if ($block_item == $block_id)
834
+					$url_fragment .= ';st_' . $block_item . '=%1$d';
835
+				elseif (!empty($context['ticket_blocks'][$block_item]['start']))
836
+					$url_fragment .= ';st_' . $block_item . '=' . $context['ticket_blocks'][$block_item]['start'];
837
+			}
838
+
839
+			$context['start'] = $context['ticket_blocks'][$block_id]['start'];
840
+			$context['ticket_blocks'][$block_id]['page_index'] = shd_no_expand_pageindex($scripturl . $primary_url . $url_fragment . '#shd_block_' . $block_id, $context['start'], $block['count'], $context['items_per_page'], true);
841
+		}
842
+	}
843
+}
844
+
845
+/**
846
+ *	Return the list of columns that is applicable to a given block.
847
+ *
848
+ *	In order to centralise the list of actions to be displayed in a block, and in its counterpart that displays all the values,
849
+ *	the lists of columns per block is kept here.
850
+ *
851
+ *	@param string $block The block we are calling from:
852
+ *	- assigned: assigned to me
853
+ *	- new: new tickets
854
+ *	- staff: pending staff
855
+ *	- user_staff: pending with user (staff view)
856
+ *	- user_user: pending with user (user view)
857
+ *	- closed: resolved tickets
858
+ *	- recycled: deleted tickets
859
+ *	- withdeleted: tickets with deleted replies
860
+ *
861
+ *	@return array An indexed array of the columns in the order they should be displayed.
862
+ *	@see shd_main_helpdesk()
863
+ *	@see shd_closed_tickets()
864
+ *	@see shd_recycle_bin()
865
+ *	@since 1.0
866
+*/
867
+function shd_get_block_columns($block)
868
+{
869
+	switch ($block)
870
+	{
871
+		case 'assigned':
872
+			return array(
873
+					'ticket_id',
874
+					'ticket_name',
875
+					'starting_user',
876
+					'replies',
877
+					'status',
878
+					'urgency',
879
+					'updated',
880
+					'actions',
881
+				);
882
+		case 'new':
883
+			return array(
884
+				'ticket_id',
885
+				'ticket_name',
886
+				'starting_user',
887
+				'assigned',
888
+				'urgency',
889
+				'updated',
890
+				'actions',
891
+			);
892
+		case 'staff':
893
+			return array(
894
+				'ticket_id',
895
+				'ticket_name',
896
+				'starting_user',
897
+				'replies',
898
+				'assigned',
899
+				'urgency',
900
+				'updated',
901
+				'actions',
902
+			);
903
+		case 'user_staff':
904
+			return array(
905
+				'ticket_id',
906
+				'ticket_name',
907
+				'starting_user',
908
+				'last_reply',
909
+				'replies',
910
+				'urgency',
911
+				'updated',
912
+				'actions',
913
+			);
914
+		case 'user_user':
915
+			return array(
916
+				'ticket_id',
917
+				'ticket_name',
918
+				'last_reply',
919
+				'replies',
920
+				'urgency',
921
+				'updated',
922
+				'actions',
923
+			);
924
+		case 'closed':
925
+			return array(
926
+				'ticket_id',
927
+				'ticket_name',
928
+				'starting_user',
929
+				'replies',
930
+				'updated',
931
+				'actions',
932
+			);
933
+		case 'recycled':
934
+			return array(
935
+				'ticket_id',
936
+				'ticket_name',
937
+				'starting_user',
938
+				'allreplies',
939
+				'assigned',
940
+				'updated',
941
+				'actions',
942
+			);
943
+		case 'withdeleted':
944
+			return array(
945
+				'ticket_id',
946
+				'ticket_name',
947
+				'starting_user',
948
+				'allreplies',
949
+				'assigned',
950
+				'updated',
951
+				'actions',
952
+			);
953
+		default:
954
+			return array();
955
+	}
956
+}
957
+?>
0 958
\ No newline at end of file
... ...
@@ -0,0 +1,702 @@
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: 1.0 Felidae                             #
17
+# File Info: Subs-SimpleDesk.php / 1.0 Felidae                #
18
+###############################################################
19
+
20
+/**
21
+ *	This file handles key functions for SimpleDesk that can be called on every page load, such as the
22
+ *	custom query function to handle ticket visibility, the counter for active tickets in the menu header, or the action log.
23
+ *
24
+ *	@package subs
25
+ *	@since 1.0
26
+ */
27
+
28
+if (!defined('SMF'))
29
+	die('Hacking attempt...');
30
+
31
+/**
32
+ *	Query wrapper around $smcFunc['db_query'] for helpdesk specific operations.
33
+ *
34
+ *	This function provides a basic wrapper around SMF's internal $smcFunc['db_query'] function, adding the parameter {query_see_ticket}
35
+ *	to it, specifically so that ticket visibility can be enforced in a query without being aware of the specific rules for the user.
36
+ *
37
+ *	If not previously loaded, user permissions are loaded with {@link shd_load_user_perms()}.
38
+ *
39
+ *	@param string $identifier SMF-style query identifier for database backend-specific replacements
40
+ *	@param string $db_string Standard SMF 2.0 style database query, including {query_see_ticket} if appropriate
41
+ *	@param array $db_values Standard SMF 2.0 style hash map of parameters to inject into the query
42
+ *	@param resource $connection A database connection variable, if supplied, to override the one used by SMF by default
43
+ *	@return resource Standard database query resource, suitable for processing with other $smcFunc['db_*'] functions
44
+ *
45
+ *	@see shd_load_user_perms()
46
+ *	@since 1.0
47
+*/
48
+function shd_db_query($identifier, $db_string, $db_values = array(), $connection = null)
49
+{
50
+	global $user_info, $smcFunc;
51
+
52
+	if (!isset($user_info['query_see_ticket']))
53
+		shd_load_user_perms();
54
+
55
+	$replacements = array(
56
+		'{query_see_ticket}' => $user_info['query_see_ticket'],
57
+	);
58
+
59
+	$db_string = str_replace(array_keys($replacements), array_values($replacements), $db_string);
60
+	$db_values['user_info_id'] = $user_info['id'];
61
+
62
+	return $smcFunc['db_query']($identifier, $db_string, $db_values, $connection);
63
+}
64
+
65
+/**
66
+ *	Defines user permissions, most importantly concerning ticket visibility
67
+ *
68
+ *	Populates specific parameters in $user_info, mostly to add {} abstract variables in $smcFunc['db_query'] data calls.
69
+ *	The foremost one of these is {query_see_ticket}, an SQL clause constructed to ensure ticket visibility is maintained given the
70
+ *	active user's permission set.
71
+ *
72
+ *	@see shd_db_query()
73
+ *	@since 1.0
74
+*/
75
+function shd_load_user_perms()
76
+{
77
+	global $user_info;
78
+	if ($user_info['is_admin'])
79
+		$user_info['query_see_ticket'] = '1=1';
80
+	elseif (shd_allowed_to('shd_view_ticket_any'))
81
+		$user_info['query_see_ticket'] = shd_allowed_to('shd_view_ticket_private_any') ? '1=1' : ('(hdt.private = 0' . (shd_allowed_to('shd_view_ticket_private_own') ? ' OR (hdt.private = 1 AND hdt.id_member_started = {int:user_info_id}))' : ')'));
82
+	elseif (shd_allowed_to('shd_view_ticket_own'))
83
+		$user_info['query_see_ticket'] = 'hdt.id_member_started = {int:user_info_id}' . (shd_allowed_to('shd_view_ticket_private_own') ? '' : ' AND hdt.private = 0');
84
+	else
85
+		$user_info['query_see_ticket'] = '1=0';
86
+
87
+	if (!shd_allowed_to('shd_access_recyclebin'))
88
+		$user_info['query_see_ticket'] .= ' AND hdt.status != 6';
89
+}
90
+
91
+/**
92
+ *	Defines the helpdesk menu item, including the number of active tickets to be displayed to the user.
93
+ *
94
+ *	Identifies the number of tickets that a user might be interested in, and generates the menu text for the main menu
95
+ *	to include this; note that the value should be cached through SMF's functions. The cache is also clearable, through
96
+ *	the {@link shd_clear_active_tickets()} function.
97
+ *
98
+ *	@return string A formatted string containing the language-specific version of "Helpdesk [x]" menu item with the x in bold
99
+ *	@see shd_clear_active_tickets()
100
+ *	@since 1.0
101
+*/
102
+function shd_get_active_tickets()
103
+{
104
+	global $modSettings, $user_info, $smcFunc, $context, $txt;
105
+
106
+	if (empty($txt['shd_helpdesk'])) // provide a last-ditch fallback in the event we can't even find the file; SimpleDesk.{language}.php should be loaded by now (falling back to english if lang-specific doesn't exist)
107
+		$txt['shd_helpdesk'] = 'Helpdesk';
108
+
109
+	if (!$modSettings['helpdesk_active'] || $context['user']['is_guest'])
110
+		return $txt['shd_helpdesk'];
111
+
112
+	// Have we already run on this page? If so we already have the answer.
113
+	if (!empty($context['active_tickets']))
114
+		return $txt['shd_helpdesk'] . $context['active_tickets'];
115
+
116
+	// Figure out the status(es) that the ticket could be. Note that we have to use numeric values rather than our proper constants
117
+	// because they are not declared at this point (only in the main helpdesk itself)
118
+	if (shd_allowed_to('shd_staff'))
119
+		$status = array(0, 1); // TICKET_STATUS_NEW, TICKET_STATUS_PENDING_STAFF (i.e. staff actually need to deal with these)
120
+	else
121
+		$status = array(2); // TICKET_STATUS_PENDING_USER (i.e. user actually needs to deal with this)
122
+
123
+	// Can we get it from the cache?
124
+	$temp = cache_get_data('shd_active_tickets_' . $user_info['id'], 180);
125
+	if ($temp !== null)
126
+	{
127
+		list($context['active_tickets'], $context['active_tickets_raw']) = $temp;
128
+		$context['menu_buttons']['helpdesk']['alttitle'] = $txt['shd_helpdesk'] . !empty($context['active_tickets_raw']) ? (' [' . $context['active_tickets_raw'] . ']') : '';
129
+		return $txt['shd_helpdesk'] . $context['active_tickets'];
130
+	}
131
+
132
+	$query = shd_db_query('', '
133
+		SELECT COUNT(id_ticket)
134
+		FROM {db_prefix}helpdesk_tickets AS hdt
135
+		WHERE {query_see_ticket} AND status IN ({array_int:status})',
136
+		array(
137
+			'status' => $status,
138
+		)
139
+	);
140
+
141
+	$context['active_tickets_raw'] = 0;
142
+
143
+	$row = $smcFunc['db_fetch_row']($query);
144
+	if (!empty($row[0]))
145
+	{
146
+		$context['menu_buttons']['helpdesk']['alttitle'] = $txt['shd_helpdesk'] . ' [' . $row[0] . ']';
147
+		$context['active_tickets'] = ' [<strong>' . $row[0] . '</strong>]';
148
+		$context['active_tickets_raw'] = $row[0]; // in case you want to do something funky in the theme later?
149
+	}
150
+	else
151
+		$context['active_tickets'] = '';
152
+
153
+	cache_put_data('shd_active_tickets_' . $user_info['id'], array($context['active_tickets'], $context['active_tickets_raw']), 120);
154
+
155
+	return $txt['shd_helpdesk'] . $context['active_tickets'];
156
+}
157
+
158
+/**
159
+ *	Clears the cache of active tickets for the menu item.
160
+ *
161
+ *	{@link shd_get_active_tickets()} generates the number of active tickets for the user display, and caches it for 180 seconds
162
+ *	normally. This function clears the cache and should be called whenever any operation modifies the state of a ticket.
163
+ *
164
+ *	@see shd_get_active_tickets()
165
+ *	@since 1.0
166
+*/
167
+function shd_clear_active_tickets($id = 0)
168
+{
169
+	global $user_info;
170
+
171
+	$members = shd_members_allowed_to('shd_staff');
172
+	if ($id != 0)
173
+		$members[] = $id;
174
+
175
+	$members[] = $user_info['id'];
176
+
177
+	$members = array_unique($members);
178
+
179
+	foreach ($members as $member)
180
+		cache_put_data('shd_active_tickets_' . $member, null, 0);
181
+}
182
+
183
+/**
184
+ *	Adds an action to the helpdesk internal action log.
185
+ *
186
+ *	This function deals with adding items to the action log maintained by the helpdesk.
187
+ *
188
+ *	@param string $action Specifies the name of the action to log, which implies the image and language string (log_$action is
189
+ *	the name of the image, and $txt['shd_log_$action'] is the string used to express the action, as listed in
190
+ *	SimpleDesk-LogAction.english.php.
191
+ *	@param array $params This is a list of named parameters in a hash array to be used in the language string later.
192
+ *
193
+ *	@see shd_load_action_log_entries()
194
+ *	@since 1.0
195
+*/
196
+function shd_log_action($action, $params)
197
+{
198
+	global $smcFunc, $context, $user_info, $modSettings;
199
+
200
+	if (!empty($modSettings['shd_disable_action_log']))
201
+		return;
202
+
203
+	// Some parts of $params we will want in the main row for sorting + lookups later. Let's see if they're here.
204
+	if (!empty($params['ticket']))
205
+	{
206
+		$ticket_id = (int) $params['ticket'];
207
+		if ($ticket_id == 0)
208
+			trigger_error('log_action(): received data with non-numeric ticket', E_USER_NOTICE);
209
+		else
210
+			unset($params['ticket']);
211
+	}
212
+	else
213
+		$ticket_id = 0;
214
+
215
+	if (!empty($params['msg']))
216
+	{
217
+		$msg_id = (int) $params['msg'];
218
+		if ($msg_id == 0)
219
+			trigger_error('log_action(): received data with non-numeric msg', E_USER_NOTICE);
220
+		else
221
+			unset($params['msg']);
222
+	}
223
+	else
224
+		$msg_id = 0;
225
+
226
+	$smcFunc['db_insert']('',
227
+		'{db_prefix}helpdesk_log_action',
228
+		array(
229
+			'log_time' => 'int', 'id_member' => 'int', 'ip' => 'string-16', 'action' => 'string', 'id_ticket' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
230
+		),
231
+		array(
232
+			time(), $user_info['id'], $user_info['ip'], $action, $ticket_id, $msg_id, serialize($params),
233
+		),
234
+		array('id_action')
235
+	);
236
+}
237
+
238
+/**
239
+ *	Determines if the current user can raise/lower the urgency of a ticket.
240
+ *
241
+ *	This function identifies whether the current user can raise or lower the urgency of a ticket based on the current urgency
242
+ *	of the ticket and whether it is their ticket; this is used in the ticket display as well as the actions linked directly to
243
+ *	modifying urgency (both AJAXively and non AJAXively)
244
+ *
245
+ *	@param int $urgency The current urgency of a ticket as an integer
246
+ *	@param bool $ticket_starter Whether the user in question is the starter of the ticket (instead of querying to establish that,
247
+ *	that detail should already be known to the calling function)
248
+ *	@param bool $closed Whether the ticket is currently closed or not
249
+ *	@param bool $deleted Whether the ticket is currently closed or not
250
+ *
251
+ *	@see shd_urgency_change_noajax()
252
+ *	@since 1.0
253
+*/
254
+function shd_can_alter_urgency($urgency, $ticket_starter, $closed, $deleted)
255
+{
256
+	global $user_info;
257
+
258
+	$can_urgency = array(
259
+		'increase' => false,
260
+		'decrease' => false,
261
+	);
262
+
263
+	if ($closed || $deleted)
264
+		return $can_urgency;
265
+
266
+	if (shd_allowed_to('shd_alter_urgency_any'))
267
+	{
268
+		if (shd_allowed_to('shd_alter_urgency_higher_any') || (shd_allowed_to('shd_alter_urgency_higher_own') && $ticket_starter == $user_info['id']))
269
+			$can_urgency = array( // can alter any urgency and can alter this one's higher urgency too
270
+				'increase' => ($urgency < TICKET_URGENCY_CRITICAL),
271
+				'decrease' => ($urgency > TICKET_URGENCY_LOW),
272
+			);
273
+		else
274
+			$can_urgency = array( // can alter any base urgency - just not this one's higher urgency
275
+				'increase' => ($urgency < TICKET_URGENCY_HIGH),
276
+				'decrease' => ($urgency > TICKET_URGENCY_LOW && $urgency < TICKET_URGENCY_VHIGH),
277
+			);
278
+	}
279
+	elseif (shd_allowed_to('shd_alter_urgency_own') && $ticket_starter == $user_info['id'])
280
+		$can_urgency = array( // ok, so this is our ticket and we can change it
281
+			'increase' => ($urgency < (shd_allowed_to('shd_alter_urgency_higher_own') ? TICKET_URGENCY_CRITICAL : TICKET_URGENCY_HIGH)),
282
+			'decrease' => ($urgency > TICKET_URGENCY_LOW && $urgency <= (shd_allowed_to('shd_alter_urgency_higher_own') ? TICKET_URGENCY_CRITICAL : TICKET_URGENCY_VHIGH)),
283
+		);
284
+
285
+	return $can_urgency;
286
+}
287
+
288
+/**
289
+ *	Queries the database to find the number of applicable tickets
290
+ *
291
+ *	This function collects counts for the different states of tickets (new, with staff, with user, etc) of all the tickets
292
+ *	visible to the user, and returns a selection of that dataset based on the values provided to $status and $is_staff.
293
+ *
294
+ *	@param string $status The relevant count of tickets to return:
295
+ *	<ul>
296
+ *	<li>'open': All tickets currently open that the user can see</li>
297
+ *	<li>'assigned': All tickets assigned to the current user</li>
298
+ *	<li>'new': All the new tickets that the user can see</li>
299
+ *	<li>'staff': All the tickets currently with staff (varies for staff vs user; user count here includes 'new' tickets)</li>
300
+ *	<li>'with_user': All the tickets pending user comment</li>
301
+ *	<li>'closed': All the tickets the user can see that are resolved</li>
302
+ *	<li>'recycled': All the tickets the user can see that are currently in the recycle bin</li>
303
+ *	<li>'withdeleted': All the tickets that have at least one deleted reply</li>
304
+ *	<li>'' or unspecified: Return the total of all tickets in the helpdesk (subject to visibility)</li>
305
+ *	</ul>
306
+ *	@param bool $is_staff If the user in question is staff or not.
307
+ *
308
+ *	@return int Number of applicable tickets.
309
+ *	@since 1.0
310
+*/
311
+function shd_count_helpdesk_tickets($status = '', $is_staff = false)
312
+{
313
+	global $smcFunc, $context;
314
+
315
+	if (empty($context['ticket_count']))
316
+	{
317
+		$context['ticket_count'] = array();
318
+		for ($i = 0; $i <= 6; $i++)
319
+			$context['ticket_count'][$i] = 0; // set the count to zero for all known states
320
+
321
+		$query = shd_db_query('', '
322
+			SELECT status, COUNT(status) AS tickets
323
+			FROM {db_prefix}helpdesk_tickets AS hdt
324
+			WHERE {query_see_ticket}
325
+			GROUP BY status
326
+			ORDER BY null',
327
+			array()
328
+		);
329
+
330
+		while ($row = $smcFunc['db_fetch_assoc']($query))
331
+			$context['ticket_count'][$row['status']] = $row['tickets'];
332
+
333
+		$smcFunc['db_free_result']($query);
334
+
335
+		$context['ticket_count']['assigned'] = 0;
336
+		if (shd_allowed_to('shd_staff'))
337
+		{
338
+			$query = shd_db_query('', '
339
+				SELECT status, COUNT(status) AS tickets
340
+				FROM {db_prefix}helpdesk_tickets AS hdt
341
+				WHERE {query_see_ticket}
342
+					AND id_member_assigned = {int:user}
343
+				GROUP BY status
344
+				ORDER BY null',
345
+				array(
346
+					'user' => $context['user']['id'],
347
+				)
348
+			);
349
+
350
+			while ($row = $smcFunc['db_fetch_assoc']($query))
351
+			{
352
+				if (!in_array($row['status'], array(3, 6))) // not TICKET_STATUS_CLOSED or TICKET_STATUS_DELETE
353
+				{
354
+					$context['ticket_count']['assigned'] += $row['tickets'];
355
+					$context['ticket_count'][$row['status']] -= $row['tickets'];
356
+				}
357
+			}
358
+
359
+			$smcFunc['db_free_result']($query);
360
+		}
361
+
362
+		if (shd_allowed_to('shd_access_recyclebin'))
363
+		{
364
+			$query = shd_db_query('', '
365
+				SELECT COUNT(id_ticket) AS tickets
366
+				FROM {db_prefix}helpdesk_tickets AS hdt
367
+				WHERE {query_see_ticket}
368
+					AND hdt.withdeleted = {int:has_deleted}
369
+					AND hdt.status != {int:ticket_deleted}',
370
+				array(
371
+					'has_deleted' => 1, // MSG_STATUS_DELETED
372
+					'ticket_deleted' => 6, // TICKET_STATUS_DELETED; we want all non deleted tickets with deleted replies
373
+				)
374
+			);
375
+			list($count) = $smcFunc['db_fetch_row']($query);
376
+			$smcFunc['db_free_result']($query);
377
+
378
+			$context['ticket_count']['withdeleted'] = $count;
379
+		}
380
+		else
381
+			$context['ticket_count']['withdeleted'] = 0;
382
+	}
383
+
384
+	switch($status)
385
+	{
386
+		case 'open':
387
+			return (
388
+				$context['ticket_count'][0] + // _NEW
389
+				$context['ticket_count'][1] + // _PENDING_STAFF
390
+				$context['ticket_count'][2] + // _PENDING_USER
391
+				$context['ticket_count'][4] + // _WITH_SUPERVISOR
392
+				$context['ticket_count'][5]   // _ESCALATED
393
+			);
394
+		case 'assigned':
395
+			return $context['ticket_count']['assigned'];
396
+		case 'new':
397
+			return $context['ticket_count'][0];
398
+		case 'staff':
399
+			return $is_staff ? $context['ticket_count'][1] : ($context['ticket_count'][0] + $context['ticket_count'][1]); // both "new" and "with staff" should appear as 'with staff' to non staff
400
+		case 'with_user':
401
+			return $context['ticket_count'][2];
402
+		case 'closed':
403
+			return $context['ticket_count'][3];
404
+		case 'recycled':
405
+			return $context['ticket_count'][6];
406
+		case 'withdeleted':
407
+			return $context['ticket_count']['withdeleted'];
408
+		default:
409
+			return array_sum($context['ticket_count']) - $context['ticket_count']['assigned'];
410
+	}
411
+}
412
+
413
+/**
414
+ *	Attempts to load a given ticket's data.
415
+ *
416
+ *	This function permission-checks, and throws appropriate errors if no ticket is specified either directly or through URL,
417
+ *	or if the ticket is not accessible either through deletion or lack of permissions.
418
+ *
419
+ *	@param int $ticket The ticket to use; if none is specified, use the one from $_REQUEST['ticket'], which will have been processed
420
+ *	into $context['ticket_id'] if it is available.
421
+ *
422
+ *	@return array A large hash map stating many ticket details
423
+ *	@since 1.0
424
+*/
425
+function shd_load_ticket($ticket = 0)
426
+{
427
+	global $context, $smcFunc, $scripturl;
428
+
429
+	// Make sure they set a ticket ID.
430
+	if ($ticket == 0 && empty($context['ticket_id']))
431
+		fatal_lang_error('shd_no_ticket', false);
432
+
433
+	// Get the ticket data. Note this implicitly checks perms too.
434
+	$query = shd_db_query('', '
435
+		SELECT hdt.id_first_msg, hdt.id_last_msg, hdt.id_member_started, hdt.subject, hdt.urgency, hdt.status,
436
+			hdt.num_replies, hdt.deleted_replies, hdt.private, hdtr.body, hdtr.id_member, hdtr.poster_time,
437
+			hdtr.modified_time, hdtr.smileys_enabled,
438
+			IFNULL(mem.real_name, hdtr.poster_name) AS starter_name, IFNULL(mem.id_member, 0) AS starter_id, hdtr.poster_ip AS starter_ip,
439
+			IFNULL(ma.real_name, 0) AS assigned_name, IFNULL(ma.id_member, 0) AS assigned_id,
440
+			IFNULL(mm.real_name, hdtr.modified_name) AS modified_name, IFNULL(mm.id_member, 0) AS modified_id
441
+		FROM {db_prefix}helpdesk_tickets AS hdt
442
+			INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hdt.id_first_msg = hdtr.id_msg)
443
+			LEFT JOIN {db_prefix}members AS mem ON (hdt.id_member_started = mem.id_member)
444
+			LEFT JOIN {db_prefix}members AS ma ON (hdt.id_member_assigned = ma.id_member)
445
+			LEFT JOIN {db_prefix}members AS mm ON (hdtr.modified_member = mm.id_member)
446
+		WHERE {query_see_ticket} AND hdt.id_ticket = {int:ticket}',
447
+		array(
448
+			'ticket' => $ticket == 0 ? $context['ticket_id'] : $ticket,
449
+		)
450
+	);
451
+
452
+	if ($smcFunc['db_num_rows']($query) == 0)
453
+	{
454
+		$smcFunc['db_free_result']($query);
455
+		fatal_lang_error('shd_no_ticket', false);
456
+	}
457
+
458
+	$ticketinfo = $smcFunc['db_fetch_assoc']($query);
459
+	$smcFunc['db_free_result']($query);
460
+
461
+	// Anything else we'll use a lot?
462
+	$ticketinfo['is_own'] = ($context['user']['id'] == $ticketinfo['starter_id']);
463
+	$ticketinfo['closed'] = $ticketinfo['status'] == TICKET_STATUS_CLOSED;
464
+	$ticketinfo['deleted'] = $ticketinfo['status'] == TICKET_STATUS_DELETED;
465
+
466
+	$ticketinfo['link_ip'] = shd_allowed_to('moderate_forum');
467
+
468
+	return $ticketinfo;
469
+}
470
+
471
+/**
472
+ *	Formats a string for bbcode and/or smileys.
473
+ *
474
+ *	Formatting is done according to the supplied settings and the master administration settings.
475
+ *
476
+ *	@param string $text Raw text with optional bbcode formatting
477
+ *	@param bool $smileys Whether smileys should be used; this is not an override to the master administration setting of
478
+ *	whether to use smileys or not, and that takes precedence.
479
+ *	@param string $cache If specified, this will provide the cache'd id that SMF should use to cache the output if it is suitably large.
480
+ *
481
+ *	@return string Will return $text as processed for bbcode (if $modSettings['shd_allow_ticket_bbc'] permits) and smileys (if
482
+ *	$modSettings['shd_allow_ticket_smileys'] and $smileys permits)
483
+ *	@since 1.0
484
+*/
485
+function shd_format_text($text, $smileys = true, $cache = '')
486
+{
487
+	global $modSettings;
488
+	if (empty($modSettings['shd_allow_ticket_bbc']))
489
+	{
490
+		if (!empty($modSettings['shd_allow_ticket_smileys']) && $smileys)
491
+			parsesmileys($text);
492
+	}
493
+	else
494
+	{
495
+		// See what tagz we can haz.
496
+		if (!empty($modSettings['shd_enabled_bbc']))
497
+			$enabled_tags = explode(',', $modSettings['shd_enabled_bbc']);
498
+		else
499
+			$enabled_tags = array(false);
500
+
501
+		// wecanhazbbc
502
+		$text = parse_bbc($text, (!empty($modSettings['shd_allow_ticket_smileys']) ? $smileys : false), $cache, $enabled_tags);
503
+	}
504
+
505
+	return $text;
506
+}
507
+
508
+/**
509
+ *	Determines if a user has a given permission within the system.
510
+ *
511
+ *	All SimpleDesk-specific permissions should be checked with this function. Any other permission check that is not specifically
512
+ *	SimpleDesk-related should use allowedTo instead.
513
+ *
514
+ *	@param mixed $permission A string or array of strings naming a permission or permissions that wish to be examined
515
+ *	@return bool True if any of the permission(s) outlined in $permission are true.
516
+ *	@see shd_is_allowed_to
517
+ *	@todo This function will likely be expanded to not use SMF's allowedTo function eventually, as its requirements will outgrow
518
+ *	or replace conventional SMF permissions.
519
+ *	@since 1.0
520
+*/
521
+function shd_allowed_to($permission)
522
+{
523
+	return allowedTo($permission);
524
+}
525
+
526
+/**
527
+ *	Enforces a user having a given permission and returning to a fatal error message if not.
528
+ *
529
+ *	All fatal-level SimpleDesk-specific permissions should be checked with this function. Any other permission check that is
530
+ *	not specifically SimpleDesk-related should use isAllowedTo instead. Note that this is a void function because should this
531
+ *	fail, SMF execution will be halted.
532
+ *
533
+ *	@param mixed $permission A string or array of strings naming a permission or permissions that wish to be examined
534
+ *	@see shd_allowed_to
535
+ *	@todo This function will likely be expanded to not use SMF's allowedTo function eventually, as its requirements will outgrow
536
+ *	or replace conventional SMF permissions.
537
+ *	@since 1.0
538
+*/
539
+function shd_is_allowed_to($permission)
540
+{
541
+	isAllowedTo($permission);
542
+}
543
+
544
+/**
545
+ *	Identifies all members who hold a given permission.
546
+ *
547
+ *	Currently lists of staff are generated by users who hold shd_staff permission. This function identifies those users through
548
+ *	an internal lookup provided by SMF.
549
+ *
550
+ *	@param mixed $permission A string naming a permission that members should hold.
551
+ *	@return array Array of zero or more user ids who hold the stated permission.
552
+ *	@see shd_allowed_to
553
+ *	@todo This function will likely be expanded to not use SMF's permissions eventually.
554
+ *	@since 1.0
555
+*/
556
+function shd_members_allowed_to($permission)
557
+{
558
+	global $sourcedir;
559
+	require_once($sourcedir . '/Subs-Members.php');
560
+	return membersAllowedTo($permission);
561
+}
562
+
563
+/**
564
+ *	Generates a profile link given user id and name.
565
+ *
566
+ *	SMF itself has loadMemberData() and loadMemberContext to perform this act, however those two functions are much larger and more
567
+ *	complex than we need more often than not, especially when all we want/need is member name (and subsequently profile link)
568
+ *
569
+ *	Since profile links should be based on the view profile permission, we will need to establish that.
570
+ *
571
+ *	@param string $name The name to display. This should be a standard SMF type name, which means already sanitised for HTML.
572
+ *	@param int $id The numeric id of the user we are linking to.
573
+ *
574
+ *	@return string Returns an HTML link to user profile if sufficient permission and both a name and id are supplied. Otherwise just the name is.
575
+ *	@since 1.0
576
+*/
577
+function shd_profile_link($name, $id = 0)
578
+{
579
+	global $user_info, $scripturl;
580
+	static $any = null;
581
+	static $own = null;
582
+
583
+	if ($any === null)
584
+	{
585
+		$any = allowedTo('profile_view_any'); // using allowedTo as it's an SMF permission, not an SD specific one
586
+		$own = allowedTo('profile_view_own');
587
+	}
588
+
589
+	if (empty($id))
590
+		return $name;
591
+	elseif ($any || ($own && $id == $user_info['id']))
592
+		return '<a href="' . $scripturl . '?action=profile;u=' . $id . '">' . $name . '</a>';
593
+	else
594
+		return $name;
595
+}
596
+
597
+/**
598
+ *	Establishes the next change of status of a ticket.
599
+ *
600
+ *	Tickets invariably have multiple changes of status during their life. All actions that could change
601
+ *	a ticket's status should call here, even if it is a straight forward, one-route-only change of status
602
+ *	since it is possible we could end up giving the user a choice one day over how statuses work, so
603
+ *	we should route everything through here all the time.
604
+ *
605
+ *	@param string $action (required), represents the action carried out by the calling function
606
+ *			Known values: new, resolve, unresolve, deleteticket, restoreticket, deletereply, restorereply, reply, merge, topictoticket (new is default)
607
+ *	@param int $starter_id Numeric id of the ticket's starter (should be provided)
608
+ *	@param int $replier_id Numeric id of the ticket's last reply author (should be provided)
609
+ *	@param int $replies Number of replies in the ticket (should be provided)
610
+ *
611
+ *	@return int Returns an integer value that corresponds to the ticket's status, relating to one of the TICKET_STATUS states.
612
+ *	@since 1.0
613
+*/
614
+function shd_determine_status($action, $starter_id = 0, $replier_id = 0, $replies = -1)
615
+{
616
+	$known_states = array(
617
+		'new',
618
+		'resolve',
619
+		'unresolve',
620
+		'deleteticket',
621
+		'restoreticket',
622
+		'deletereply',
623
+		'restorereply',
624
+		'reply',
625
+		'topictoticket',
626
+	);
627
+
628
+	if (!in_array($action, $known_states))
629
+		$action = 'new';
630
+
631
+	switch ($action)
632
+	{
633
+		case 'new':
634
+			return TICKET_STATUS_NEW; // it's a new ticket, what more can I say?
635
+		case 'resolve':
636
+			return TICKET_STATUS_CLOSED; // yup, all done
637
+		case 'deleteticket':
638
+			return TICKET_STATUS_DELETED; // bye bye
639
+		case 'merge': // used in handling deleted replies
640
+		case 'deletereply':
641
+		case 'restorereply':
642
+		case 'unresolve':
643
+		case 'restoreticket':
644
+		case 'reply':
645
+		case 'topictoticket':
646
+			if ($replies == 0)
647
+				return TICKET_STATUS_NEW;
648
+			else
649
+			{
650
+				$staff = shd_members_allowed_to('shd_staff');
651
+				if (in_array($replier_id, $staff))
652
+					$new_status = $starter_id == $replier_id ? TICKET_STATUS_PENDING_STAFF : TICKET_STATUS_PENDING_USER; // i.e. if they're staff but it's their own ticket they're replying to, it's not with user.
653
+				else
654
+					$new_status = TICKET_STATUS_PENDING_STAFF;
655
+
656
+				return $new_status;
657
+			}
658
+	}
659
+}
660
+
661
+/**
662
+ *	Wrapper function for constructPageIndex to forcibly block the extensible ... item in page indexes
663
+ *
664
+ *	SimpleDesk uses SMF's core page index function in numerous places, but unlike SMF, it often places it in containers
665
+ *	that have backgrounds driven by menu_block.png, meaning that they are often fixed in height. Under some circumstances
666
+ *	layout can be broken, so this function forcibly ensures the block can never expand to force wrapping.
667
+ *
668
+ *	@param string $base_url Form of URL pageindex links should take, using $1%d to represent the start point identifier.
669
+ *	@param int &$start Position to start. If not a multiple of the number per page, it will be forced to become a multiple.
670
+ *	@param int $max_value Number of items in total to paginate for.
671
+ *	@param int $num_per_page Number of items to be shown on a page.
672
+ *	@param bool $flexible_start Whether a more flexible % option is to be used in the base URL.
673
+ *
674
+ *	@return string The constructed page index, without Javascript expander(s).
675
+ *	@since 1.0
676
+*/
677
+function shd_no_expand_pageindex($base_url, &$start, $max_value, $num_per_page, $flexible_start = false)
678
+{
679
+	return preg_replace('~<span([^<]+)~i', '<span style="font-weight: bold;"> ... ', constructPageIndex($base_url, $start, $max_value, $num_per_page, $flexible_start));
680
+}
681
+
682
+/**
683
+ *	Wrapper for loadLanguage to ensure English language files are always loaded regardless of user language settings as a fallback
684
+ *
685
+ *	As of 2.0 RC2, there is detection code in SMF to ensure the Modifications language file is always loaded, but the same detection is not applied to all language files. This ensures it will be for SD files.
686
+ *
687
+ *	@param string $langfile Name of a language file to load, typically SimpleDesk prefixed, though could be used for any SMF language file.
688
+ *	@since 1.1
689
+*/
690
+function shd_load_language($langfile)
691
+{
692
+	global $modSettings, $user_info;
693
+	if (empty($modSettings['disable_language_fallback']))
694
+	{
695
+		$cur_language = isset($user_info['language']) ? $user_info['language'] : $language;
696
+		if ($cur_language !== 'english')
697
+			loadLanguage($langfile, 'english', false);
698
+	}
699
+	loadLanguage($langfile);
700
+}
701
+
702
+?>
0 703
\ No newline at end of file
... ...
@@ -0,0 +1,184 @@
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: 1.0 Felidae                             #
17
+# File Info: Subs-SimpleDeskAdmin.php / 1.0 Felidae           #
18
+###############################################################
19
+
20
+/**
21
+ *	This file deals with some of the items required by the helpdesk, but are primarily supporting
22
+ *	functions; they're not the principle functions that drive the admin area.
23
+ *
24
+ *	@package subs
25
+ *	@since 1.0
26
+ */
27
+
28
+if (!defined('SMF'))
29
+	die('Hacking attempt...');
30
+
31
+/**
32
+ *	Load the items from the helpdesk action log
33
+ *
34
+ *	It is subject to given parameters (start, number of items, order/sorting), parses the language strings and adds the
35
+ *	parameter information provided.
36
+ *
37
+ *	@param int $start Number of items into the log to start (for pagination)
38
+ *	@param int $items_per_page How many items to load
39
+ *	@param string $sort SQL clause to state which column(s) to order the data by
40
+ *	@param string $order SQL clause to state whether the order is ascending or descending
41
+ *
42
+ *	@return array A hash array of the log items, with the auto-incremented id being the key:
43
+ *	<ul>
44
+ *	<li>id: Numeric identifier of the log item</li>
45
+ *	<li>time: Formatted time of the event (as per usual for SMF, formatted for user's timezone)</li>
46
+ *	<li>member: hash array:
47
+ *		<ul>
48
+ *			<li>id: Id of the user that committed the action</li>
49
+ *			<li>name: Name of the user</li>
50
+ *			<li>link: Link to the profile of the user that committed the action</li>
51
+ *			<li>ip: User IP address recorded when the action was carried out</li>
52
+ *			<li>group: Name of the group of the user (uses primary group, failing that post count group)</li>
53
+ *		</ul>
54
+ *	</li>
55
+ *	<li>action: Raw name of the action (for use with collecting the image later)</li>
56
+ *	<li>id_ticket: Numeric id of the ticket this action refers to</li>
57
+ *	<li>id_msg: Numeric id of the individual reply this action refers to</li>
58
+ *	<li>extra: Array of extra parameters for the log action</li>
59
+ *	<li>action_text: Formatted text of the log item (parsed with parameters)</li>
60
+ *	</ul>
61
+ *
62
+ *	@see shd_log_action()
63
+ *	@see shd_count_action_log_entries()
64
+ *	@since 1.0
65
+*/
66
+function shd_load_action_log_entries($start, $items_per_page, $sort, $order)
67
+{
68
+	global $smcFunc, $txt, $scripturl, $context;
69
+	
70
+	// Load languages incase they aren't there (Read: ticket-specific logs)
71
+	shd_load_language('SimpleDeskLogAction');
72
+	shd_load_language('SimpleDeskAdmin');	
73
+
74
+	// Without further screaming and waving, fetch the actions.
75
+	$request = shd_db_query('','
76
+		SELECT la.id_action, la.log_time, la.id_member, la.ip, la.action, la.id_ticket, la.id_msg, la.extra,
77
+		mem.real_name, mg.group_name
78
+		FROM {db_prefix}helpdesk_log_action AS la
79
+		LEFT JOIN {db_prefix}members AS mem ON(mem.id_member = la.id_member)
80
+		LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:reg_group_id} THEN mem.id_post_group ELSE mem.id_group END)
81
+		ORDER BY {raw:sort} {raw:order}
82
+		LIMIT {int:start}, {int:items_per_page}',
83
+		array(
84
+			'reg_group_id' => 0,
85
+			'sort' => $sort,
86
+			'start' => $start,
87
+			'items_per_page' => $items_per_page,
88
+			'order' => $order,
89
+		)
90
+	);
91
+
92
+	$actions = array();
93
+
94
+	while ($row = $smcFunc['db_fetch_assoc']($request))
95
+	{
96
+		$row['extra'] = @unserialize($row['extra']);
97
+		$row['extra'] = is_array($row['extra']) ? $row['extra'] : array();
98
+
99
+		$actions[$row['id_action']] = array(
100
+			'id' => $row['id_action'],
101
+			'time' => timeformat($row['log_time']),
102
+			'member' => array(
103
+				'id' => $row['id_member'],
104
+				'name' => $row['real_name'],
105
+				'link' => shd_profile_link($row['real_name'], $row['id_member']),
106
+				'ip' => !empty($row['ip']) ? $row['ip'] : $txt['shd_admin_actionlog_unknown'],
107
+				'group' => $row['group_name'],
108
+			),
109
+			'action' => $row['action'],
110
+			'id_ticket' => $row['id_ticket'],
111
+			'id_msg' => $row['id_msg'],
112
+			'extra' => $row['extra'],
113
+			'action_text' => '',
114
+			'action_icon' => 'log_' . $row['action'] . '.png',
115
+			'can_remove' => $row['log_time'] < $context['waittime'],
116
+		);
117
+	}
118
+
119
+	// Do some formatting of the action string.
120
+	foreach ($actions as $k => $action)
121
+	{
122
+		if (empty($actions[$k]['action_text']))
123
+			$actions[$k]['action_text'] = isset($txt['shd_log_' . $action['action']]) ? $txt['shd_log_' . $action['action']] : $action['action'];
124
+
125
+			$actions[$k]['action_text'] = str_replace('{scripturl}', $scripturl, $actions[$k]['action_text']);
126
+
127
+		if (isset($action['extra']['subject']))
128
+		{
129
+			$actions[$k]['action_text'] = str_replace('{ticket}', $actions[$k]['id_ticket'], $actions[$k]['action_text']);
130
+			$actions[$k]['action_text'] = str_replace('{msg}', $actions[$k]['id_msg'], $actions[$k]['action_text']);
131
+
132
+			if (isset($actions[$k]['extra']['subject']))
133
+				$actions[$k]['action_text'] = str_replace('{subject}', $actions[$k]['extra']['subject'], $actions[$k]['action_text']);
134
+
135
+			if (isset($actions[$k]['extra']['urgency']))
136
+				$actions[$k]['action_text'] = str_replace('{urgency}', $txt['shd_urgency_' . $actions[$k]['extra']['urgency']], $actions[$k]['action_text']);
137
+		}
138
+		if (isset($action['extra']['user_name']))
139
+		{
140
+			$actions[$k]['action_text'] = str_replace('{profile_link}', shd_profile_link($actions[$k]['extra']['user_name'], (isset($actions[$k]['extra']['user_id']) ? $actions[$k]['extra']['user_id'] : 0)), $actions[$k]['action_text']);
141
+			$actions[$k]['action_text'] = str_replace('{user_name}', $actions[$k]['extra']['user_name'], $actions[$k]['action_text']);
142
+		}
143
+		if (isset($action['extra']['user_id']))
144
+			$actions[$k]['action_text'] = str_replace('{user_id}', $actions[$k]['extra']['user_id'], $actions[$k]['action_text']);
145
+		if (isset($actions[$k]['extra']['board_name']))
146
+			$actions[$k]['action_text'] = str_replace('{board_name}', $actions[$k]['extra']['board_name'], $actions[$k]['action_text']);
147
+		if (isset($actions[$k]['extra']['board_id']))
148
+			$actions[$k]['action_text'] = str_replace('{board_id}', $actions[$k]['extra']['board_id'], $actions[$k]['action_text']);
149
+	}
150
+	return $actions;
151
+}
152
+
153
+/**
154
+ *	Returns the total number of items in the helpdesk log.
155
+ *
156
+ *	This function gets the total number of items logged in the helpdesk log, for the purposes of establishing the number of
157
+ *	pages there should be in the page-index.
158
+ *
159
+ *	@return int Number of entries in the helpdesk action log table.
160
+ *	@see shd_load_action_log_entries()
161
+ *	@since 1.0
162
+*/
163
+function shd_count_action_log_entries()
164
+{
165
+	global $smcFunc;
166
+
167
+	// Without further screaming and waving, fetch the actions.
168
+	$request = shd_db_query('','
169
+		SELECT COUNT(*)
170
+		FROM {db_prefix}helpdesk_log_action AS la
171
+		LEFT JOIN {db_prefix}members AS mem ON(mem.id_member = la.id_member)
172
+		LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:reg_group_id} THEN mem.id_post_group ELSE mem.id_group END)',
173
+		array(
174
+			'reg_group_id' => 0,
175
+		)
176
+	);
177
+
178
+	list ($entry_count) = $smcFunc['db_fetch_row']($request);
179
+	$smcFunc['db_free_result']($request);
180
+
181
+	return $entry_count;
182
+}
183
+
184
+?>
... ...
@@ -0,0 +1,513 @@
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: 1.0 Felidae                             #
17
+# File Info: Subs-SimpleDeskPost.php / 1.0 Felidae            #
18
+###############################################################
19
+
20
+/**
21
+ *	This file handles probably the two most critical functions in SimpleDesk: the one that adds new posts to the database
22
+ *	and one that saves updated posts; also contains miscellaneous code that applies generally in posting.
23
+ *
24
+ *	@package subs
25
+ *	@since 1.0
26
+ */
27
+
28
+if (!defined('SMF'))
29
+	die('Hacking attempt...');
30
+
31
+
32
+/**
33
+ *	Creates a new ticket or reply in the database.
34
+ *
35
+ *	This function handles all of the creation of posts and tickets within SimpleDesk, even with respect to managing tickets spawned
36
+ *	from forum topics being moved, as well as newly deleted tickets (formed as a split when replies are deleted). New tickets' contents
37
+ *	as well as replies to tickets generally hold the same format.
38
+ *
39
+ *	All three parameters are by <b>reference</b> meaning they WILL be updated if things change. Note that this function
40
+ *	is not validating that they are sensible values; it is up to the calling function to ascertain that.
41
+ *
42
+ *	@param array &$msgOptions A hash array by reference, containing details of the post you wish to add.
43
+ *	<ul>
44
+ *	<li>id: Not required on input (and is ignored) - and will be overwritten with the new message id when the function completes.</li>
45
+ *	<li>body: Required string, the principle body content of the message to post. Assumed to have been cleaned already (with $smcFunc['htmlspecialchars'] and preparsecode)</li>
46
+ *	<li>smileys_enabled: Optional, boolean denoting whether smileys should be active on this post; defaults to false</li>
47
+ *	<li>time: Optional, timestamp of the post. If omitted, time() will be used instead (for "now" based on server clock)</li>
48
+ *	<li>modified: Optional, hash array containing items relating to modification (if 'modified' key exists, all of these should be set)
49
+ *		<ul>
50
+ *			<li> time: Unsigned int timestamp of the change</li>
51
+ *			<li> name: String; user name of the user making the change; if omitted, modified will be ignored</li>
52
+ *			<li> id: Unsigned int user id of the user making the change; if not provided, id MUST be. If id isn't, or it doesn't exist, modified will be ignored entirely</li>
53
+ *		</ul>
54
+ *	</li>
55
+ *	<li> attachments: Optional, array of attachment ids that need attaching to this message; if omitted no changes will occur
56
+ *	</ul>
57
+ *
58
+ *	@param array &$ticketOptions A hash array by reference, containing details of the ticket as a whole.
59
+ *	<ul>
60
+ *	<li>id: Required if replying to a ticket, 0 if a new ticket (will default to 0 if not specified)</li>
61
+ *	<li>mark_as_read: Optional boolean, whether to mark the ticket as read by the person posting it ($posterOptions['id'] is required to use this)</li>
62
+ *	<li>subject: Semi-optional string with the new subject in; required for a new ticket, ignored if adding a reply. If set, assumed to have been cleaned already (with $smcFunc['htmlspecialchars'] and strtr)</li>
63
+ *	<li>private: Semi-optional boolean with ticket privacy (true = private); required for a new ticket, ignored if adding a reply.</li>
64
+ *	<li>status: Integer to denote new status of the ticket, defaults to TICKET_STATUS_NEW. Calling function to determine new status.</li>
65
+ *	<li>urgency: Semi-optional integer with the ticket urgency; required for a new ticket, ignored if adding a reply. If not stated on a new ticket, TICKET_URGENCY_LOW will be used.</li>
66
+ *	<li>assigned: Optional integer of user id, used to create a ticket with assignment, ignored if not a new ticket.</li>
67
+ *	</ul>
68
+ *
69
+ *	@param array &$posterOptions A hash array by reference, containing details of the person the reply is written by.
70
+ *	<ul>
71
+ *	<li>id: User id to credit the post to. Uses 0 if not specified.</li>
72
+ *	<li>ip: IP address the post came from. Uses the current user's IP address if not specified.</li>
73
+ *	<li>name: Name to credit against the post. If not specified, and a user id was supplied, look that up in the member table, otherwise just use 'Guest'.</li>
74
+ *	<li>email: Email address to list against the post. If not specified, and a user id was supplied, look that up in the member table, otherwise just use ''.</li>
75
+ *	</ul>
76
+ *	@return bool True on success, false on failure.
77
+ *	@since 1.0
78
+ */
79
+function shd_create_ticket_post(&$msgOptions, &$ticketOptions, &$posterOptions)
80
+{
81
+	global $user_info, $txt, $modSettings, $smcFunc, $context, $user_profile;
82
+
83
+	// Clean them incoming vars up good 'n' proper
84
+	$msgOptions['smileys_enabled'] = !empty($msgOptions['smileys_enabled']);
85
+	$msgOptions['attachments'] = empty($msgOptions['attachments']) ? array() : $msgOptions['attachments'];
86
+	$msgOptions['time'] = empty($msgOptions['time']) ? time() : (int) $msgOptions['time'];
87
+	$ticketOptions['id'] = empty($ticketOptions['id']) ? 0 : (int) $ticketOptions['id'];
88
+	$ticketOptions['private'] = !empty($ticketOptions['private']);
89
+	$ticketOptions['urgency'] = empty($ticketOptions['urgency']) ? TICKET_URGENCY_LOW : (int) $ticketOptions['urgency'];
90
+	$ticketOptions['assigned'] = empty($ticketOptions['assigned']) ? 0 : (int) $ticketOptions['assigned'];
91
+	$ticketOptions['status'] = empty($ticketOptions['status']) ? TICKET_STATUS_NEW : (int) $ticketOptions['status'];
92
+	$posterOptions['id'] = empty($posterOptions['id']) ? 0 : (int) $posterOptions['id'];
93
+	$posterOptions['ip'] = empty($posterOptions['ip']) ? $user_info['ip'] : $posterOptions['ip'];
94
+
95
+	// If nothing was filled in as name/e-mail address, try the member table.
96
+	if (!isset($posterOptions['name']) || $posterOptions['name'] == '' || (empty($posterOptions['email']) && !empty($posterOptions['id'])))
97
+	{
98
+		if (empty($posterOptions['id']))
99
+		{
100
+			$posterOptions['id'] = 0;
101
+			$posterOptions['name'] = $txt['guest_title'];
102
+			$posterOptions['email'] = '';
103
+		}
104
+		elseif ($posterOptions['id'] != $user_info['id'])
105
+		{
106
+			if (empty($user_profile[$posterOptions['id']]))
107
+				loadMemberData($posterOptions['id'], false, 'minimal');
108
+
109
+			// Couldn't find the current poster?
110
+			if (empty($user_profile[$posterOptions['id']]))
111
+			{
112
+				trigger_error('shd_create_ticket_post(): Invalid member id ' . $posterOptions['id'], E_USER_NOTICE);
113
+				$posterOptions['id'] = 0;
114
+				$posterOptions['name'] = $txt['guest_title'];
115
+				$posterOptions['email'] = '';
116
+			}
117
+			else
118
+			{
119
+				$posterOptions['name'] = $user_profile[$posterOptions['id']]['real_name'];
120
+				$posterOptions['email'] = $user_profile[$posterOptions['id']]['email_address'];
121
+			}
122
+		}
123
+		else
124
+		{
125
+			$posterOptions['name'] = $user_info['name'];
126
+			$posterOptions['email'] = $user_info['email'];
127
+		}
128
+	}
129
+
130
+	// Is there modified name data? (For topic->ticket)
131
+	$modified = true;
132
+	if (isset($msgOptions['modified']))
133
+	{
134
+		$msgOptions['modified']['time'] = empty($msgOptions['modified']['time']) ? 0 : (int) $msgOptions['modified']['time'];
135
+		$msgOptions['modified']['id'] = empty($msgOptions['modified']['id']) ? 0 : (int) $msgOptions['modified']['id'];
136
+		$msgOptions['modified']['name'] = empty($msgOptions['modified']['name']) ? '' : $msgOptions['modified']['name'];
137
+
138
+		$cancel = false;
139
+		if (empty($msgOptions['modified']['time']) || (empty($msgOptions['modified']['name']) && empty($msgOptions['modified']['id'])))
140
+			$modified = false;
141
+
142
+		if ($modified)
143
+		{
144
+			// So they have a time, and name or id (or even both). Let's see what we need.
145
+			if (empty($msgOptions['modified']['name']))
146
+			{
147
+				loadMemberData($msgOptions['modified']['id'], false, 'minimal');
148
+				if (empty($user_profile[$msgOptions['modified']['id']]))
149
+					$modified = false; // oops, they gave us a user id that doesn't exist -- and we don't have a name
150
+			}
151
+			// Otherwise, we have a name and no id, which is fine. Can't be doing with trying to figure out usernames.
152
+		}
153
+	}
154
+	else
155
+		$modified = false;
156
+
157
+	if (!$modified)
158
+	$msgOptions['modified'] = array(
159
+		'id' => 0,
160
+		'name' => '',
161
+		'time' => 0,
162
+	);
163
+
164
+	// It's do or die time: forget any user aborts!
165
+	$previous_ignore_user_abort = ignore_user_abort(true);
166
+
167
+	$new_ticket = empty($ticketOptions['id']);
168
+
169
+	// OK, so let's add the reply. Even if it's a new ticket and stuff, let's still add the msg first so we have our friendly msg id
170
+	$smcFunc['db_insert']('',
171
+		'{db_prefix}helpdesk_ticket_replies',
172
+		array(
173
+			'id_ticket' => 'int', 'id_member' => 'int', 'body' => 'string-65534',
174
+			'poster_name' => 'string-255', 'poster_email' => 'string-255', 'poster_time' => 'int', 'poster_ip' => 'string-255',
175
+			'smileys_enabled' => 'int', 'modified_member' => 'int', 'modified_name' => 'string', 'modified_time' => 'int',
176
+		),
177
+		array(
178
+			$ticketOptions['id'], $posterOptions['id'], $msgOptions['body'],
179
+			$posterOptions['name'], $posterOptions['email'], $msgOptions['time'], $posterOptions['ip'],
180
+			$msgOptions['smileys_enabled'] ? 1 : 0, $msgOptions['modified']['id'], $msgOptions['modified']['name'], $msgOptions['modified']['time'],
181
+		),
182
+		array('id_msg')
183
+	);
184
+	$msgOptions['id'] = $smcFunc['db_insert_id']('{db_prefix}messages', 'id_msg');
185
+
186
+	// Something went wrong creating the message...
187
+	if (empty($msgOptions['id']))
188
+		return false;
189
+
190
+	// Insert a new ticket (if the ID was left empty)
191
+	if ($new_ticket)
192
+	{
193
+		$smcFunc['db_insert']('',
194
+			'{db_prefix}helpdesk_tickets',
195
+			array(
196
+				'id_first_msg' => 'int', 'id_member_started' => 'int', 'id_last_msg' => 'int',  'id_member_updated' => 'int',
197
+				'id_member_assigned' => 'int', 'subject' => 'string-100', 'urgency' => 'int', 'status' => 'int',
198
+				'private' => 'int',
199
+			),
200
+			array(
201
+				$msgOptions['id'], $posterOptions['id'], $msgOptions['id'], $posterOptions['id'],
202
+				$ticketOptions['assigned'], $ticketOptions['subject'], $ticketOptions['urgency'], $ticketOptions['status'],
203
+				$ticketOptions['private'] ? 1 : 0,
204
+			),
205
+			array('id_ticket')
206
+		);
207
+		$ticketOptions['id'] = $smcFunc['db_insert_id']('{db_prefix}helpdesk_tickets', 'id_ticket');
208
+
209
+		// The ticket couldn't be created for some reason.
210
+		if (empty($ticketOptions['id']))
211
+		{
212
+			// We should delete the post that did work, though...
213
+			shd_db_query('', '
214
+				DELETE FROM {db_prefix}helpdesk_ticket_replies
215
+				WHERE id_msg = {int:id_msg}',
216
+				array(
217
+					'id_msg' => $msgOptions['id'],
218
+				)
219
+			);
220
+
221
+			return false;
222
+		}
223
+
224
+		// Fix the message with the ticket.
225
+		shd_db_query('', '
226
+			UPDATE {db_prefix}helpdesk_ticket_replies
227
+			SET id_ticket = {int:id_ticket}
228
+			WHERE id_msg = {int:id_msg}',
229
+			array(
230
+				'id_ticket' => $ticketOptions['id'],
231
+				'id_msg' => $msgOptions['id'],
232
+			)
233
+		);
234
+
235
+	}
236
+	// The ticket already exists, it only needs a little updating.
237
+	else
238
+	{
239
+		shd_db_query('', '
240
+			UPDATE {db_prefix}helpdesk_tickets
241
+			SET
242
+				id_member_updated = {int:poster_id},
243
+				id_last_msg = {int:id_msg},
244
+				num_replies = num_replies + 1,
245
+				status = {int:status}
246
+			WHERE id_ticket = {int:id_ticket}',
247
+			array(
248
+				'poster_id' => $posterOptions['id'],
249
+				'id_msg' => $msgOptions['id'],
250
+				'status' => $ticketOptions['status'],
251
+				'id_ticket' => $ticketOptions['id'],
252
+			)
253
+		);
254
+	}
255
+
256
+	// Fix the attachments.
257
+	if (!empty($msgOptions['attachments']))
258
+	{
259
+		$array = array();
260
+		foreach ($msgOptions['attachments'] as $attach)
261
+			$array[] = array($attach, $msgOptions['id'], $ticketOptions['id']);
262
+
263
+		$smcFunc['db_insert']('replace',
264
+			'{db_prefix}helpdesk_attachments',
265
+			array(
266
+				'id_attach' => 'int', 'id_msg' => 'int', 'id_ticket' => 'int',
267
+			),
268
+			$array,
269
+			array('id_attach')
270
+		);
271
+	}
272
+
273
+	// Mark inserted ticket as read (only for the user listed as the author -- oftentimes will be the current user though)
274
+	if (!empty($ticketOptions['mark_as_read']) && !empty($posterOptions['id']))
275
+	{
276
+		// Since it's likely they *read* it before replying, let's try an UPDATE first.
277
+		if (!$new_ticket)
278
+		{
279
+			shd_db_query('', '
280
+				UPDATE {db_prefix}helpdesk_log_read
281
+				SET id_msg = {int:id_msg}
282
+				WHERE id_member = {int:current_member}
283
+					AND id_ticket = {int:id_ticket}',
284
+				array(
285
+					'current_member' => $posterOptions['id'],
286
+					'id_msg' => $msgOptions['id'],
287
+					'id_ticket' => $ticketOptions['id'],
288
+				)
289
+			);
290
+
291
+			$flag = $smcFunc['db_affected_rows']() != 0;
292
+		}
293
+
294
+		if (empty($flag))
295
+		{
296
+			$smcFunc['db_insert']('replace',
297
+				'{db_prefix}helpdesk_log_read',
298
+				array('id_ticket' => 'int', 'id_member' => 'int', 'id_msg' => 'int'),
299
+				array($ticketOptions['id'], $posterOptions['id'], $msgOptions['id']),
300
+				array('id_ticket', 'id_member')
301
+			);
302
+			return false;
303
+		}
304
+	}
305
+
306
+	ignore_user_abort($previous_ignore_user_abort);
307
+
308
+	// Success.
309
+	return true;
310
+}
311
+
312
+/**
313
+ *	Updates a ticket/reply item in the database.
314
+ *
315
+ *	This function allows modification of all post/ticket details - and can be used independently; it is possible (and even done
316
+ *	in SimpleDesk) to update just a ticket or just a post from this function. All three parameters are by <b>reference</b>
317
+ *	meaning they WILL be updated if things change. Note that this function is not validating that they are sensible values; it is
318
+ *	up to the calling function to ascertain that.
319
+ *
320
+ *	@param array &$msgOptions - a hash array by reference, stating zero or more details to change on a message (if the change is strictly ticket-only, the entire $msgOptions array can be an empty array):
321
+ *	<ul>
322
+ *	<li>id: Required if changing a message; the principle numeric id of the message to modify</li>
323
+ *	<li>body: Optional, the principle body content of the message to change; if omitted, no change will occur. If set, assumed to have been cleaned already (with $smcFunc['htmlspecialchars'] and preparsecode)</li>
324
+ *	<li>modified: Optional, hash array containing items relating to modification (if 'modified' key exists, all of these should be set)
325
+ *		<ul>
326
+ *			<li>time: Unsigned int timestamp of the change</li>
327
+ *			<li>name: String; user name of the user making the change</li>
328
+ *			<li>id: Unsigned int user id of the user making the change</li>
329
+ *		</ul>
330
+ *	</li>
331
+ *	<li>smileys_enabled: Optional, boolean denoting whether smileys should be active on this post; if omitted no change will occur</li>
332
+ *	<li>attachments: Optional, array of attachment ids that need attaching to this message; if omitted no changes will occur</li>
333
+ *	</ul>
334
+ *
335
+ *	@param array &$ticketOptions - a hash array by reference, stating one or more details necessary to change on a ticket:
336
+ *	<ul>
337
+ *	<li>id: Required in all cases, numeric ticket id that the edit relates to</li>
338
+ *	<li>subject: Optional string with the new subject in; if omitted no change will occur. If set, assumed to have been cleaned already (with $smcFunc['htmlspecialchars'] and strtr)</li>
339
+ *	<li>urgency: Optional integer with the new urgency in; if omitted no change will occur</li>
340
+ *	<li>status: Optional integer with the new status in; if omitted no change will occur</li>
341
+ *	<li>ssigned: Optional integer with the user id of assigned user; if omitted no change will occur (note, you would declare this as 0 to unassign a ticket - set to 0 is not omitted)</li>
342
+ *	<li>private: Optional boolean as to privacy of ticket: true = private, false = not private (note, you still declare this to change it)</li>
343
+ *	</ul>
344
+ *
345
+ *	@param array &$posterOptions - a hash array by reference of details to change on the poster of a message:
346
+ *	<ul>
347
+ *	<li>name: Optional string, name of credited poster (in absence of id, this will be used); if omitted no change will occur</li>
348
+ *	<li>email: Optional string, email address of poster; if omitted no change will occur</li>
349
+ *	</ul>
350
+ *
351
+ *	@return bool True on success, false on failure.
352
+ *	@since 1.0
353
+*/
354
+function shd_modify_ticket_post(&$msgOptions, &$ticketOptions, &$posterOptions)
355
+{
356
+	global $user_info, $txt, $modSettings, $smcFunc, $context;
357
+
358
+	$messages_columns = array();
359
+	$ticket_columns = array();
360
+
361
+	$ticketOptions['id'] = !empty($ticketOptions['id']) ? (int) $ticketOptions['id'] : 0;
362
+	if ($ticketOptions['id'] == 0)
363
+		return false;
364
+
365
+	if (isset($posterOptions['name']))
366
+		$messages_columns['poster_name'] = $posterOptions['name'];
367
+	if (isset($posterOptions['email']))
368
+		$messages_columns['poster_email'] = $posterOptions['email'];
369
+	if (isset($msgOptions['body']))
370
+		$messages_columns['body'] = $msgOptions['body'];
371
+	if (!empty($msgOptions['modified']))
372
+	{
373
+		$messages_columns['modified_time'] = $msgOptions['modified']['time'];
374
+		$messages_columns['modified_name'] = $msgOptions['modified']['name'];
375
+		$messages_columns['modified_member'] = $msgOptions['modified']['id'];
376
+	}
377
+	if (isset($msgOptions['smileys_enabled']))
378
+		$messages_columns['smileys_enabled'] = empty($msgOptions['smileys_enabled']) ? 0 : 1;
379
+	if (isset($ticketOptions['subject']))
380
+		$ticket_columns['subject'] = $ticketOptions['subject'];
381
+	if (isset($ticketOptions['status']))
382
+		$ticket_columns['status'] = $ticketOptions['status'];
383
+	if (isset($ticketOptions['urgency']))
384
+		$ticket_columns['urgency'] = $ticketOptions['urgency'];
385
+	if (isset($ticketOptions['assigned']))
386
+		$ticket_columns['id_member_assigned'] = $ticketOptions['assigned'];
387
+	if (isset($ticketOptions['private']))
388
+		$ticket_columns['private'] = empty($ticketOptions['private']) ? 0 : 1;
389
+
390
+	// Fix the attachments.
391
+	if (!empty($msgOptions['attachments']))
392
+	{
393
+		$array = array();
394
+		foreach ($msgOptions['attachments'] as $attach)
395
+			$array[] = array($attach, $msgOptions['id'], $ticketOptions['id']);
396
+
397
+		$smcFunc['db_insert']('replace',
398
+			'{db_prefix}helpdesk_attachments',
399
+			array(
400
+				'id_attach' => 'int', 'id_msg' => 'int', 'id_ticket' => 'int',
401
+			),
402
+			$array,
403
+			array('id_attach')
404
+		);
405
+	}
406
+
407
+	if (empty($messages_columns) && empty($ticket_columns))
408
+		return true;
409
+
410
+	// It's do or die time: forget any user aborts!
411
+	$previous_ignore_user_abort = ignore_user_abort(true);
412
+
413
+	// OK, let's get all this set up ready for SQL magic
414
+	// Which columns need to be ints?
415
+	$messageInts = array('modified_time', 'modified_member', 'smileys_enabled');
416
+	$msg_update_parameters = array(
417
+		'id_msg' => empty($msgOptions['id']) ? 0 : (int) $msgOptions['id'],
418
+	);
419
+
420
+	foreach ($messages_columns as $var => $val)
421
+	{
422
+		$messages_columns[$var] = $var . ' = {' . (in_array($var, $messageInts) ? 'int' : 'string') . ':var_' . $var . '}';
423
+		$msg_update_parameters['var_' . $var] = $val;
424
+	}
425
+
426
+	$ticketInts = array('status', 'urgency');
427
+	$ticket_update_parameters = array(
428
+		'id_ticket' => $ticketOptions['id'],
429
+	);
430
+
431
+	foreach ($ticket_columns as $var => $val)
432
+	{
433
+		$ticket_columns[$var] = $var . ' = {' . (in_array($var, $ticketInts) ? 'int' : 'string') . ':var_' . $var . '}';
434
+		$ticket_update_parameters['var_' . $var] = $val;
435
+	}
436
+
437
+	// GO GO GO! (message first)
438
+	if (!empty($messages_columns))
439
+	{
440
+		shd_db_query('', '
441
+			UPDATE {db_prefix}helpdesk_ticket_replies
442
+			SET ' . implode(', ', $messages_columns) . '
443
+			WHERE id_msg = {int:id_msg}',
444
+			$msg_update_parameters
445
+		);
446
+	}
447
+
448
+	if (!empty($ticket_columns))
449
+	{
450
+		shd_db_query('', '
451
+			UPDATE {db_prefix}helpdesk_tickets
452
+			SET ' . implode(', ', $ticket_columns) . '
453
+			WHERE id_ticket = {int:id_ticket}',
454
+			$ticket_update_parameters
455
+		);
456
+	}
457
+
458
+	ignore_user_abort($previous_ignore_user_abort);
459
+
460
+	// Success.
461
+	return true;
462
+}
463
+
464
+/**
465
+ *	Identifies the range of options that a user could have for ticket urgency and updates $context['ticket_form'] accordingly.
466
+ *
467
+ *	Ticket urgency permissions are checked, and an array is built suitable for $context['ticket_form'], which is the principle
468
+ *	format used in the ticket posting/reply posting functions in SimpleDesk-Post.php.
469
+ *
470
+ *	There is no return function; $context['ticket_form']['urgency'] is updated, both the ['options'] and ['can_change'] keys
471
+ *	may be modified.
472
+ *
473
+ *	@param bool $self_ticket (default false) Permissions will be different for many users depending on whether it is one
474
+ *	of their own tickets or not. This allows the code to state whether it is a ticket owned by the current user or not.
475
+ *
476
+ *	@see SimpleDesk-Display.php
477
+ *	@since 1.0
478
+*/
479
+function shd_get_urgency_options($self_ticket = false)
480
+{
481
+	global $context;
482
+	$context['ticket_form']['urgency']['options'] = array(
483
+		TICKET_URGENCY_LOW => 'shd_urgency_0',
484
+		TICKET_URGENCY_MEDIUM => 'shd_urgency_1',
485
+		TICKET_URGENCY_HIGH => 'shd_urgency_2',
486
+		TICKET_URGENCY_VHIGH => 'shd_urgency_3',
487
+		TICKET_URGENCY_SEVERE => 'shd_urgency_4',
488
+		TICKET_URGENCY_CRITICAL => 'shd_urgency_5',
489
+	);
490
+
491
+	if (shd_allowed_to('shd_alter_urgency_higher_any') || ($self_ticket && shd_allowed_to('shd_alter_urgency_higher_own')))
492
+	{
493
+		$context['ticket_form']['urgency']['can_change'] = true;
494
+	}
495
+	elseif (shd_allowed_to('shd_alter_urgency_any') || ($self_ticket && shd_allowed_to('shd_alter_urgency_own')))
496
+	{
497
+		if (!empty($context['ticket_form']['urgency']['setting']) && $context['ticket_form']['urgency']['setting'] > TICKET_URGENCY_HIGH)
498
+			$context['ticket_form']['urgency']['can_change'] = false;
499
+		else
500
+		{
501
+			$context['ticket_form']['urgency']['can_change'] = true;
502
+			unset(
503
+				$context['ticket_form']['urgency']['options'][TICKET_URGENCY_VHIGH],
504
+				$context['ticket_form']['urgency']['options'][TICKET_URGENCY_SEVERE],
505
+				$context['ticket_form']['urgency']['options'][TICKET_URGENCY_CRITICAL]
506
+			);
507
+		}
508
+	}
509
+	else
510
+		$context['ticket_form']['urgency']['can_change'] = false;
511
+}
512
+
513
+?>
0 514
\ No newline at end of file
... ...
@@ -0,0 +1,696 @@
1
+<?php
2
+// Version: 1.0 Felidae; SimpleDesk's administration area
3
+
4
+/**
5
+ *	Displays SimpleDesk's administration panel, options pages, action log and the get-support page.
6
+ *
7
+ *	@package template
8
+ *	@since 1.0
9
+*/
10
+
11
+/**
12
+ *	Display the main information center for the administration panel.
13
+ *
14
+ *	This function handles output of data populated by {@link shd_admin_info()}:
15
+ *	- upgraded SD version advisory
16
+ *	- latest news from SimpleDesk.net
17
+ *	- basic version check
18
+ *	- count of open/closed/recycled tickets in the helpdesk in total
19
+ *	- list of current helpdesk staff
20
+ *	- credits
21
+ *
22
+ *	@see shd_admin_info()
23
+ *	@since 1.0
24
+*/
25
+function template_shd_admin()
26
+{
27
+	global $context, $settings, $options, $scripturl, $txt, $modSettings;
28
+
29
+	// Make our admin feel welcome
30
+	echo '
31
+	<div id="admincenter">';
32
+
33
+	// Welcome, sir.
34
+	echo '
35
+	<div class="tborder">
36
+		<h3 class="catbg"><span class="left"></span>
37
+			<span class="ie6_header floatleft">
38
+				<img src="', $settings['default_images_url'], '/admin/simpledesk.png" class="icon" alt="*" /> ', $txt['shd_admin_info'], '
39
+			</span>
40
+		</h3>
41
+		<p class="windowbg description shd_info_box">
42
+			<strong>', $txt['hello_guest'], ' ', $context['user']['name'], '!</strong><br />
43
+			', $txt['shd_admin_info_desc'], '
44
+		</p>
45
+	</div>
46
+	<br />';
47
+
48
+	// Update?
49
+	echo '
50
+			<div id="sd_update_section" class="tborder" style="display: none;"></div>';
51
+
52
+	echo '
53
+		<div id="admin_main_section">';
54
+
55
+	// Display the "live news" from simpledesk.net
56
+	echo '
57
+			<div id="sd_live_news" class="floatleft">
58
+				<div class="tborder">
59
+				<div class="cat_bar grid_header">
60
+					<h3 class="catbg">
61
+						<img src="', $settings['default_images_url'], '/simpledesk/live.png" alt="*" />
62
+						', $txt['shd_live_from'], '
63
+						<span class="righttext"><a href="', $scripturl, '?action=helpadmin;help=shd_admin_help_live" onclick="return reqWin(this.href);"><img src="', $settings['images_url'], '/helptopics.gif" alt="', $txt['help'], '" /></a></span>
64
+					</h3>
65
+				</div>
66
+				<div class="windowbg">
67
+						<div class="content">
68
+							<div id="sdAnnouncements">', $txt['shd_no_connect'], '</div>
69
+						</div>
70
+					<span class="botslice"><span></span></span>
71
+				</div>
72
+				</div>
73
+			</div>';
74
+
75
+	// Show the user version information from their server.
76
+	echo '
77
+			<div id="sd_supportVersionsTable" class="floatright">
78
+				<div class="tborder">
79
+				<div class="cat_bar grid_header">
80
+					<h3 class="catbg">
81
+						<img src="', $settings['default_images_url'], '/simpledesk/modification.png" alt="*" />
82
+						', $txt['shd_mod_information'], '
83
+						<span class="righttext"><a href="', $scripturl, '?action=helpadmin;help=shd_admin_help_modification" onclick="return reqWin(this.href);"><img src="', $settings['images_url'], '/helptopics.gif" alt="', $txt['help'], '" /></a></span>
84
+					</h3>
85
+				</div>
86
+				<div class="windowbg">
87
+					<div class="content">
88
+						<div id="sd_version_details">
89
+							<strong>', $txt['support_versions'], ':</strong><br />
90
+							', $txt['shd_your_version'], ':
91
+							<em id="yourVersion" class="shd_nowrap">', $context['shd_version'], '</em><br />
92
+							', $txt['shd_current_version'], ':
93
+							<em id="sdVersion" class="shd_nowrap">??</em><br /><br />
94
+							<strong>', $txt['shd_ticket_information'], ':</strong><br />
95
+							', $txt['shd_total_tickets'], ':
96
+							<em id="totalTickets" class="shd_nowrap">
97
+								<a href="javascript: shd_ticket_total_information();" >', $context['total_tickets'], '</a>
98
+							</em>
99
+							<div id="shd_ticket_total_information" style="display: none;">
100
+								&nbsp;&nbsp;&nbsp;', $txt['shd_open_tickets'], ': <em>', $context['open_tickets'], '</em><br />
101
+								&nbsp;&nbsp;&nbsp;', $txt['shd_closed_tickets'], ': <em>', $context['closed_tickets'], '</em><br />
102
+								&nbsp;&nbsp;&nbsp;', $txt['shd_recycled_tickets'], ': <em>', $context['recycled_tickets'], '</em><br />
103
+							</div>
104
+							<br />';
105
+
106
+	// Display all the members who can manage the helpdesk.
107
+	// NOTE: This is currently (15/1/10) uncapped, meaning it's just the full list direct from SimpleDesk-Admin.php.
108
+	// That gets the data. Up to here how it should be displayed.
109
+	echo '
110
+							<br />
111
+							<strong>', $txt['shd_staff_list'], ':</strong>
112
+							', implode(', ', $context['staff']);
113
+
114
+	echo '
115
+						</div>
116
+					</div>
117
+					<span class="botslice"><span></span></span>
118
+				</div>
119
+			</div>
120
+			</div>
121
+		</div>
122
+		<div class="shd_credits_break">&nbsp;</div>';
123
+
124
+
125
+	echo '
126
+		<div id="sd_credits">
127
+			<div class="tborder">
128
+			<div class="title_bar grid_header">
129
+				<h3 class="titlebg sd_no_margin">
130
+					<span class="left"></span>
131
+					<img src="', $settings['default_images_url'], '/simpledesk/credits.png" alt="*" />
132
+					', $txt['shd_credits'], '
133
+					<span class="righttext"><a href="', $scripturl, '?action=helpadmin;help=shd_admin_help_credits" onclick="return reqWin(this.href);"><img src="', $settings['images_url'], '/helptopics.gif" alt="', $txt['help'], '" /></a></span>
134
+				</h3>
135
+			</div>';
136
+
137
+		foreach ($context['shd_credits'] as $section)
138
+		{
139
+			echo '
140
+				<div class="roundframe">
141
+					<div class="content">
142
+						', $section['pretext'], '
143
+						<hr />';
144
+
145
+				foreach ($section['groups'] as $group)
146
+				{
147
+					echo '
148
+						<div class="description">';
149
+
150
+					if (isset($group['title']))
151
+					echo '
152
+								<strong>', $group['title'], '</strong>', !empty($group['desc']) ? ' - <em class="smalltext">' . $group['desc'] . '</em>' : '', '<hr />';
153
+
154
+					// Try to make this read nicely.
155
+					if (count($group['members']) <= 2)
156
+						echo implode(' ' . $txt['shd_credits_and'] . ' ', $group['members']);
157
+					else
158
+					{
159
+						$last_peep = array_pop($group['members']);
160
+						echo implode(', ', $group['members']), ', ', $txt['shd_credits_and'], ' ', $last_peep;
161
+					}
162
+
163
+					echo '
164
+
165
+							</div>';
166
+				}
167
+
168
+					echo '
169
+					</div>
170
+				</div>
171
+				<span class="lowerframe"><span></span></span>
172
+				</div>
173
+			</div>';
174
+		}
175
+
176
+	echo '
177
+		</div>
178
+	<br class="clear" />';
179
+
180
+	// The below functions include all the scripts needed from the simpledesk.net site. The language and format are passed for internationalization.
181
+	if (empty($modSettings['disable_smf_js']))
182
+		echo '
183
+		<script type="text/javascript" src="http://www.simpledesk.net/sd/current-version.js"></script>
184
+		<script type="text/javascript" src="http://www.simpledesk.net/sd/latest-news.js"></script>';
185
+
186
+	// This sets the announcements and current versions themselves ;).
187
+	echo '
188
+		<script type="text/javascript"><!-- // --><![CDATA[
189
+
190
+			var oAdminIndex = new sd_AdminIndex({
191
+				sSelf: \'oAdminCenter\',
192
+
193
+				bLoadAnnouncements: true,
194
+				sAnnouncementTemplate: ', JavaScriptEscape('
195
+					<dl>
196
+						%content%
197
+					</dl>
198
+				'), ',
199
+				sAnnouncementMessageTemplate: ', JavaScriptEscape('
200
+					<dt><a href="%href%" target="_blank">%subject%</a> ' . $txt['on'] . ' %time% ' . $txt['by'] . ' %author%</dt>
201
+					<dd>
202
+						%message%<br />
203
+						<a href="%readmore%" class="smalltext" target="_blank">' . $txt['shd_admin_readmore'] . '</a>
204
+					</dd>
205
+				'), ',
206
+				sAnnouncementContainerId: \'sdAnnouncements\',
207
+
208
+				bLoadVersions: true,
209
+				sSdVersionContainerId: \'sdVersion\',
210
+				sYourVersionContainerId: \'yourVersion\',
211
+				sVersionOutdatedTemplate: ', JavaScriptEscape('
212
+					<span class="alert">%currentVersion%</span>
213
+				'), ',
214
+
215
+				bLoadUpdateNotification: true,
216
+				sUpdateNotificationContainerId: \'sd_update_section\',
217
+				sUpdateNotificationDefaultTitle: ', JavaScriptEscape($txt['shd_update_available']), ',
218
+				sUpdateNotificationDefaultMessage: ', JavaScriptEscape($txt['shd_update_message']), ',
219
+				sUpdateNotificationTemplate: ', JavaScriptEscape('
220
+					<div class="cat_bar grid_header" id="update_title">
221
+						<h3 class="catbg">
222
+							<img src="' . $settings['default_images_url'] . '/simpledesk/update.png" alt="" />
223
+							%title%
224
+							<span class="righttext"><a href="' . $scripturl . '?action=helpadmin;help=shd_admin_help_update" onclick="return reqWin(this.href);"><img src="'. $settings['images_url'] . '/helptopics.gif" alt="' . $txt['help'] . '" /></a></span>
225
+						</h3>
226
+					</div>
227
+					<div class="windowbg" id="update_container">
228
+						<div class="content" id="update_content">
229
+							<p id="update_critical_alert" class="alert" style="display: none;">!!</p>
230
+							<h3 id="update_critical_title" style="display: none;">%criticaltitle%</h3>
231
+							<div id="update_message" class="smalltext">
232
+								<p>
233
+									%message%
234
+								</p>
235
+							</div>
236
+						</div>
237
+						<span class="botslice"><span></span></span>
238
+					</div>
239
+				'), ',
240
+				sUpdateNotificationLink: ', JavaScriptEscape($scripturl . '?action=admin;area=packages;pgdownload;auto;package=%package%;' . $context['session_var'] . '=' . $context['session_id']), ',
241
+				sUpdateInformationLink: \'%information%\',
242
+			});
243
+		// ]]></script>';
244
+}
245
+
246
+/**
247
+ *	Display options as set up by the options functions.
248
+ *
249
+ *	This is a modified version of the standard SMF template for displaying settings, mostly so we have access to a custom BBC template.
250
+ *
251
+ *	In short, SMF's functions call the relevant function in {@link SimpleDesk-Admin.php} to gather which options should be displayed, store in $context, then pass it here.
252
+ *
253
+ *	The same template services all of the defined areas inside Admin: SimpleDesk: Options.
254
+ *
255
+ *	@see shd_admin_options()
256
+ *	@since 1.0
257
+*/
258
+function template_shd_show_settings()
259
+{
260
+	global $context, $txt, $settings, $scripturl;
261
+
262
+	echo '
263
+	<script type="text/javascript"><!-- // --><![CDATA[';
264
+
265
+	if (!empty($context['settings_pre_javascript']))
266
+		echo $context['settings_pre_javascript'];
267
+
268
+	// If we have BBC selection we have a bit of JS.
269
+	if (!empty($context['bbc_sections']))
270
+	{
271
+		echo '
272
+		function toggleBBCDisabled(section, disable)
273
+		{
274
+			for (var i = 0; i < document.forms.bbcForm.length; i++)
275
+			{
276
+				if (typeof(document.forms.bbcForm[i].name) == "undefined" || (document.forms.bbcForm[i].name.substr(0, 11) != "enabledTags") || (document.forms.bbcForm[i].name.indexOf(section) != 11))
277
+					continue;
278
+
279
+				document.forms.bbcForm[i].disabled = disable;
280
+			}
281
+			document.getElementById("bbc_" + section + "_select_all").disabled = disable;
282
+		}';
283
+	}
284
+	echo '
285
+	// ]]></script>';
286
+
287
+	if (!empty($context['settings_insert_above']))
288
+		echo $context['settings_insert_above'];
289
+
290
+	echo '
291
+	<div id="admincenter">
292
+		<form action="', $context['post_url'], '" method="post" accept-charset="', $context['character_set'], '"', !empty($context['force_form_onsubmit']) ? ' onsubmit="' . $context['force_form_onsubmit'] . '"' : '', '>';
293
+
294
+	// Is there a custom title?
295
+	if (isset($context['settings_title']))
296
+		echo '
297
+		<div class="tborder">
298
+			<div class="cat_bar grid_header">
299
+				<h3 class="catbg">
300
+					<img src="', $settings['default_images_url'], '/simpledesk/', $context['settings_icon'], '" class="icon" alt="*" /> ', $context['settings_title'], '
301
+				</h3>
302
+			</div>';
303
+
304
+	// Have we got some custom code to insert?
305
+	if (!empty($context['settings_message']))
306
+		echo '
307
+			<div class="information">', $context['settings_message'], '</div>';
308
+
309
+	// Now actually loop through all the variables.
310
+	$is_open = false;
311
+	foreach ($context['config_vars'] as $config_var)
312
+	{
313
+		// Is it a title or a description?
314
+		if (is_array($config_var) && ($config_var['type'] == 'title' || $config_var['type'] == 'desc'))
315
+		{
316
+			// Not a list yet?
317
+			if ($is_open)
318
+			{
319
+				$is_open = false;
320
+				echo '
321
+					</dl>
322
+				</div>
323
+				<span class="botslice"><span></span></span>
324
+			</div>
325
+			</div>';
326
+			}
327
+
328
+			// A title?
329
+			if ($config_var['type'] == 'title')
330
+			{
331
+				echo '
332
+					<div class="cat_bar">
333
+						<h3 class="', !empty($config_var['class']) ? $config_var['class'] : 'catbg', '"', !empty($config_var['force_div_id']) ? ' id="' . $config_var['force_div_id'] . '"' : '', '>
334
+							', ($config_var['help'] ? '<a href="' . $scripturl . '?action=helpadmin;help=' . $config_var['help'] . '" onclick="return reqWin(this.href);" class="help"><img src="' . $settings['images_url'] . '/helptopics.gif" alt="' . $txt['help'] . '" /></a>' : ''), '
335
+							', $config_var['label'], '
336
+						</h3>
337
+					</div>';
338
+			}
339
+			// A description?
340
+			else
341
+			{
342
+				echo '
343
+					<p class="description">
344
+						', $config_var['label'], '
345
+					</p>';
346
+			}
347
+
348
+			continue;
349
+		}
350
+
351
+		// Not a list yet?
352
+		if (!$is_open)
353
+		{
354
+			$is_open = true;
355
+
356
+			if (!isset($context['settings_title']))
357
+				echo '<div class="tborder">';
358
+
359
+			echo '
360
+			<div class="windowbg2">
361
+				<div class="content">
362
+					<dl class="settings">';
363
+		}
364
+
365
+		// Hang about? Are you pulling my leg - a callback?!
366
+		if (is_array($config_var) && $config_var['type'] == 'callback')
367
+		{
368
+			if (function_exists('template_callback_' . $config_var['name']))
369
+				call_user_func('template_callback_' . $config_var['name']);
370
+
371
+			continue;
372
+		}
373
+
374
+		if (is_array($config_var))
375
+		{
376
+			// First off, is this a span like a message?
377
+			if (in_array($config_var['type'], array('message', 'warning')))
378
+			{
379
+				echo '
380
+						<dd', $config_var['type'] == 'warning' ? ' class="alert"' : '', (!empty($config_var['force_div_id']) ? ' id="' . $config_var['force_div_id'] . '_dd"' : ''), '>
381
+							', $config_var['label'], '
382
+						</dd>';
383
+			}
384
+			// Otherwise it's an input box of some kind.
385
+			else
386
+			{
387
+				echo '
388
+						<dt', is_array($config_var) && !empty($config_var['force_div_id']) ? ' id="' . $config_var['force_div_id'] . '"' : '', '>';
389
+
390
+				// Some quick helpers...
391
+				$javascript = $config_var['javascript'];
392
+				$disabled = !empty($config_var['disabled']) ? ' disabled="disabled"' : '';
393
+				$subtext = !empty($config_var['subtext']) ? '<br /><span class="smalltext"> ' . $config_var['subtext'] . '</span>' : '';
394
+
395
+				// Show the [?] button.
396
+				if ($config_var['help'])
397
+					echo '
398
+							<a id="setting_', $config_var['name'], '" href="', $scripturl, '?action=helpadmin;help=', $config_var['help'], '" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.gif" alt="', $txt['help'], '" border="0" /></a><span', ($config_var['disabled'] ? ' style="color: #777777;"' : ($config_var['invalid'] ? ' class="error"' : '')), '><label for="', $config_var['name'], '">', $config_var['label'], '</label>', $subtext, ($config_var['type'] == 'password' ? '<br /><em>' . $txt['admin_confirm_password'] . '</em>' : ''), '</span>
399
+						</dt>';
400
+				else
401
+					echo '
402
+							<a id="setting_', $config_var['name'], '"></a> <span', ($config_var['disabled'] ? ' style="color: #777777;"' : ($config_var['invalid'] ? ' class="error"' : '')), '><label for="', $config_var['name'], '">', $config_var['label'], '</label>', $subtext, ($config_var['type'] == 'password' ? '<br /><em>' . $txt['admin_confirm_password'] . '</em>' : ''), '</span>
403
+						</dt>';
404
+
405
+				echo '
406
+						<dd', (!empty($config_var['force_div_id']) ? ' id="' . $config_var['force_div_id'] . '_dd"' : ''), '>',
407
+							$config_var['preinput'];
408
+
409
+				// Show a check box.
410
+				if ($config_var['type'] == 'check')
411
+					echo '
412
+							<input type="checkbox"', $javascript, $disabled, ' name="', $config_var['name'], '" id="', $config_var['name'], '"', ($config_var['value'] ? ' checked="checked"' : ''), ' value="1" class="input_check" />';
413
+				// Escape (via htmlspecialchars.) the text box.
414
+				elseif ($config_var['type'] == 'password')
415
+					echo '
416
+							<input type="password"', $disabled, $javascript, ' name="', $config_var['name'], '[0]"', ($config_var['size'] ? ' size="' . $config_var['size'] . '"' : ''), ' value="*#fakepass#*" onfocus="this.value = \'\'; this.form.', $config_var['name'], '.disabled = false;" class="input_password" /><br />
417
+							<input type="password" disabled="disabled" id="', $config_var['name'], '" name="', $config_var['name'], '[1]"', ($config_var['size'] ? ' size="' . $config_var['size'] . '"' : ''), ' class="input_password" />';
418
+				// Show a selection box.
419
+				elseif ($config_var['type'] == 'select')
420
+				{
421
+					echo '
422
+							<select name="', $config_var['name'], '" id="', $config_var['name'], '" ', $javascript, $disabled, (!empty($config_var['multiple']) ? ' multiple="multiple"' : ''), '>';
423
+					foreach ($config_var['data'] as $option)
424
+						echo '
425
+								<option value="', $option[0], '"', (($option[0] == $config_var['value'] || (!empty($config_var['multiple']) && in_array($option[0], $config_var['value']))) ? ' selected="selected"' : ''), '>', $option[1], '</option>';
426
+					echo '
427
+							</select>';
428
+				}
429
+				// Text area?
430
+				elseif ($config_var['type'] == 'large_text')
431
+				{
432
+					echo '
433
+							<textarea rows="', ($config_var['size'] ? $config_var['size'] : 4), '" cols="30" ', $javascript, $disabled, ' name="', $config_var['name'], '" id="', $config_var['name'], '">', $config_var['value'], '</textarea>';
434
+				}
435
+				// Permission group?
436
+				elseif ($config_var['type'] == 'permissions')
437
+				{
438
+					theme_inline_permissions($config_var['name']);
439
+				}
440
+				// BBC selection?
441
+				elseif ($config_var['type'] == 'bbc')
442
+				{
443
+					echo '
444
+							<fieldset id="', $config_var['name'], '">
445
+								<legend><strong>', $txt['bbcTagsToUse_select'], '</strong></legend>
446
+									<ul class="reset">';
447
+
448
+					foreach ($context['bbc_columns'] as $bbcColumn)
449
+					{
450
+						foreach ($bbcColumn as $bbcTag)
451
+							echo '
452
+										<li class="list_bbc align_left shd_bbc_list">
453
+											<input type="checkbox" name="', $config_var['name'], '_enabledTags[]" id="tag_', $config_var['name'], '_', $bbcTag['tag'], '" value="', $bbcTag['tag'], '"', in_array($bbcTag['tag'], $context['enabled_tags'][$config_var['name']]) ? ' checked="checked"' : '', ' class="input_check" /> <label for="tag_', $config_var['name'], '_', $bbcTag['tag'], '">', $bbcTag['tag'], '</label>', $bbcTag['show_help'] ? ' (<a href="' . $scripturl . '?action=helpadmin;help=tag_' . $bbcTag['tag'] . '" onclick="return reqWin(this.href);">?</a>)' : '', '
454
+										</li>';
455
+					}
456
+					echo '			</ul>
457
+								<br class="clear" /><input type="checkbox" id="select_all" onclick="invertAll(this, this.form, \'', $config_var['name'], '_enabledTags\');"', $context['all_enabled'][$config_var['name']] ? ' checked="checked"' : '', ' class="input_check" /> <label for="select_all"><em>', $txt['bbcTagsToUse_select_all'], '</em></label>
458
+							</fieldset>';
459
+				}
460
+				// A simple message?
461
+				elseif ($config_var['type'] == 'var_message')
462
+					echo '
463
+							<div', !empty($config_var['name']) ? ' id="' . $config_var['name'] . '"' : '', '>', $config_var['var_message'], '</div>';
464
+				// Assume it must be a text box.
465
+				else
466
+					echo '
467
+							<input type="text"', $javascript, $disabled, ' name="', $config_var['name'], '" id="', $config_var['name'], '" value="', $config_var['value'], '"', ($config_var['size'] ? ' size="' . $config_var['size'] . '"' : ''), ' class="input_text" />';
468
+
469
+				echo isset($config_var['postinput']) ? '
470
+							' . $config_var['postinput'] : '',
471
+					'</dd>';
472
+			}
473
+		}
474
+
475
+		else
476
+		{
477
+			// Just show a separator.
478
+			if ($config_var == '')
479
+				echo '
480
+					</dl>
481
+					<hr class="hrcolor" />
482
+					<dl class="settings">';
483
+			else
484
+				echo '
485
+						<dd>
486
+							<strong>' . $config_var . '</strong>
487
+						</dd>';
488
+		}
489
+	}
490
+
491
+	if ($is_open)
492
+		echo '
493
+						</dl>';
494
+
495
+	if (empty($context['settings_save_dont_show']))
496
+		echo '
497
+						<hr class="hrcolor" />
498
+						<div class="righttext">
499
+							<input type="submit" value="', $txt['save'], '"', (!empty($context['save_disabled']) ? ' disabled="disabled"' : ''), (!empty($context['settings_save_onclick']) ? ' onclick="' . $context['settings_save_onclick'] . '"' : ''), ' class="button_submit" />
500
+						</div>';
501
+
502
+	if ($is_open)
503
+		echo '
504
+					</div>
505
+				<span class="botslice"><span></span></span>
506
+			</div>
507
+			</div>';
508
+
509
+	echo '
510
+		<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
511
+		</form>
512
+	</div>
513
+	<br class="clear" />';
514
+
515
+	if (!empty($context['settings_post_javascript']))
516
+		echo '
517
+	<script type="text/javascript"><!-- // --><![CDATA[
518
+	', $context['settings_post_javascript'], '
519
+	// ]]></script>';
520
+
521
+	if (!empty($context['settings_insert_below']))
522
+		echo $context['settings_insert_below'];
523
+
524
+}
525
+
526
+/**
527
+ *	Display the action log.
528
+ *
529
+ *	Little real work is done in this template; mostly is just iterating through the already-processed contents of the action log as done by {@link shd_admin_action_log()}.
530
+ *
531
+ *	@see shd_admin_action_log()
532
+ *	@since 1.0
533
+*/
534
+function template_shd_action_log()
535
+{
536
+	global $settings, $txt, $context, $scripturl, $sort_types, $modSettings;
537
+
538
+	// The sort stuff here is huge.
539
+	echo '
540
+				<div class="tborder">
541
+					<div class="cat_bar grid_header">
542
+						<h3 class="catbg">
543
+							<span class="floatright smalltext">', $txt['pages'], ': ', $context['page_index'], '</span>
544
+							<img src="', $settings['default_images_url'], '/simpledesk/log.png" class="icon" alt="*" />
545
+							', $txt['shd_admin_actionlog_title'], '
546
+						</h3>
547
+					</div>
548
+					<p class="description shd_actionloginfo">
549
+						', $txt['shd_admin_actionlog_desc'], '<br />
550
+						', !empty($modSettings['shd_disable_action_log']) ? '<span class="smalltext">' . $txt['shd_action_log_disabled'] . '</span>' : '', '
551
+					</p>
552
+					<table class="shd_ticketlist" cellspacing="0" width="100%">
553
+						<tr class="titlebg">
554
+							<td width="24%" colspan="2">
555
+								<img src="', $settings['default_images_url'], '/simpledesk/action.png" class="shd_smallicon" alt="" />
556
+								<a href="', $scripturl, '?action=admin;area=helpdesk_actionlog', $context['sort'] == $sort_types['action'] && !isset($_REQUEST['asc']) ? ';sort=action;asc' : ';sort=action', '">
557
+									', $txt['shd_admin_actionlog_action'], '
558
+								</a>
559
+								', ($context['sort'] == $sort_types['action'] ? '<img src="' . $settings['default_images_url'] . '/' . (isset($_REQUEST['asc']) ? 'sort_up.gif' : 'sort_down.gif' ). '" alt="" />' : ''), '
560
+							</td>
561
+							<td width="20%">
562
+								<img src="', $settings['default_images_url'], '/simpledesk/time.png" class="shd_smallicon" alt="" />
563
+								<a href="', $scripturl, '?action=admin;area=helpdesk_actionlog', $context['sort'] == $sort_types['time'] && !isset($_REQUEST['asc']) ? ';sort=time;asc' : ';sort=time', '">
564
+									', $txt['shd_admin_actionlog_date'], '
565
+								</a>
566
+								', ($context['sort'] == $sort_types['time'] ? '<img src="' . $settings['default_images_url'] . '/' . (isset($_REQUEST['asc']) ? 'sort_up.gif' : 'sort_down.gif' ). '" alt="" />' : ''), '
567
+							</td>
568
+							<td width="20%">
569
+								<img src="', $settings['default_images_url'], '/simpledesk/user.png" class="shd_smallicon" alt="" />
570
+								<a href="', $scripturl, '?action=admin;area=helpdesk_actionlog', $context['sort'] == $sort_types['member'] && !isset($_REQUEST['asc']) ? ';sort=member;asc' : ';sort=member', '">
571
+									', $txt['shd_admin_actionlog_member'], '
572
+								</a>
573
+								', ($context['sort'] == $sort_types['member'] ? '<img src="' . $settings['default_images_url'] . '/' . (isset($_REQUEST['asc']) ? 'sort_up.gif' : 'sort_down.gif' ). '" alt="" />' : ''), '
574
+							</td>
575
+							<td width="16%">
576
+								<img src="', $settings['default_images_url'], '/simpledesk/position.png" class="shd_smallicon" alt="" />
577
+								<a href="', $scripturl, '?action=admin;area=helpdesk_actionlog', $context['sort'] == $sort_types['position'] && !isset($_REQUEST['asc']) ? ';sort=position;asc' : ';sort=position', '">
578
+									', $txt['shd_admin_actionlog_position'], '
579
+								</a>
580
+								', ($context['sort'] == $sort_types['position'] ? '<img src="' . $settings['default_images_url'] . '/' . (isset($_REQUEST['asc']) ? 'sort_up.gif' : 'sort_down.gif' ). '" alt="" />' : ''), '
581
+							</td>
582
+							<td width="20%">
583
+								<img src="', $settings['default_images_url'], '/simpledesk/ip.png" class="shd_smallicon" alt="" />
584
+								<a href="', $scripturl, '?action=admin;area=helpdesk_actionlog', $context['sort'] == $sort_types['ip'] && !isset($_REQUEST['asc']) ? ';sort=ip;asc' : ';sort=ip', '">
585
+									', $txt['shd_admin_actionlog_ip'], '
586
+								</a>
587
+								', ($context['sort'] == $sort_types['ip'] ? '<img src="' . $settings['default_images_url'] . '/' . (isset($_REQUEST['asc']) ? 'sort_up.gif' : 'sort_down.gif' ). '" alt="" />' : ''), '
588
+							</td>
589
+							<td width="2%">&nbsp;</td>
590
+						</tr>';
591
+
592
+			if (empty($context['actions']))
593
+				echo '
594
+						<tr class="windowbg2">
595
+							<td colspan="7" class="shd_noticket">', $txt['shd_admin_actionlog_none'], '</td>
596
+						</tr>';
597
+			else
598
+			{
599
+				$use_bg2 = true; // start with windowbg2 to differentiate between that and windowbg2
600
+				foreach ($context['actions'] AS $action)
601
+				{
602
+					echo '
603
+						<tr class="', ($use_bg2 ? 'windowbg2' : 'windowbg'), '">
604
+							<td width="1%" class="shd_nowrap">
605
+								<img src="', $settings['default_images_url'], '/simpledesk/', $action['action_icon'], '" alt="" class="shd_smallicon" />
606
+							</td>
607
+							<td class="smalltext">', $action['action_text'], '</td>
608
+							<td>', $action['time'], '</td>
609
+							<td>', $action['member']['link'], '</td>
610
+							<td>', $action['member']['group'], '</td>
611
+							<td>', $action['member']['ip'], '</td>
612
+							<td>', $action['can_remove'] && $context['can_delete'] ? '<a href="' . $scripturl . '?action=admin;area=helpdesk_actionlog;remove='. $action['id'] . '"><img src="' . $settings['default_images_url'] . '/simpledesk/delete.png" alt="' . $txt['shd_delete_item'] . '" /></a>' : '', '</td>
613
+						</tr>';
614
+
615
+					$use_bg2 = !$use_bg2;
616
+				}
617
+			}
618
+
619
+	echo '
620
+					<tr class="titlebg">
621
+						<td colspan="7">
622
+							<span class="floatright smalltext">', $txt['pages'], ': ', $context['page_index'], '</span>
623
+							<span class="smalltext shd_empty_log"><img src="', $settings['default_images_url'], '/simpledesk/delete.png" alt="X" /> <a href="', $scripturl, '?action=admin;area=helpdesk_actionlog', $context['url_sort'], $context['url_order'], ';removeall" onclick="return confirm(', JavaScriptEscape(sprintf($txt['shd_admin_actionlog_removeall_confirm'],$context['hoursdisable'])), ');">', $txt['shd_admin_actionlog_removeall'], '</a></span>
624
+						</td>
625
+					</tr>
626
+					</table>
627
+					</div>';
628
+}
629
+
630
+/**
631
+ *	Displays the get-support form for posting directly to the SimpleDesk support board.
632
+ *
633
+ *	This is little more than a simple HTML form, most of the real work is hidden behind the scenes in SimpleDesk's own site.
634
+ *
635
+ *	@see shd_admin_support()
636
+ *	@since 1.0
637
+*/
638
+function template_shd_support()
639
+{
640
+	global $context, $settings, $txt, $forum_version;
641
+
642
+	// Information box.
643
+	echo '
644
+	<div class="tborder">
645
+		<h3 class="catbg"><span class="left"></span>
646
+			<span class="ie6_header floatleft">
647
+				<img src="', $settings['default_images_url'], '/simpledesk/support.png" class="icon" alt="*" /> ', $txt['shd_admin_support'], '
648
+			</span>
649
+		</h3>
650
+		<p class="windowbg description shd_info_box">
651
+			', $txt['shd_admin_support_desc'], '
652
+		</p>
653
+	</div>
654
+	<br />
655
+	<div class="shd_admin_leftcolumn floatleft">
656
+		<div class="tborder">
657
+			<div class="cat_bar grid_header">
658
+				<h3 class="catbg">
659
+					<img src="', $settings['default_images_url'], '/simpledesk/edit.png" alt="*" /> ', $txt['shd_admin_support_form_title'], '
660
+				</h3>
661
+			</div>
662
+				<div class="roundframe">
663
+					<form action="', $context['shd_support_url'], '" method="post" class="content">
664
+						<dl id="post_header">
665
+							<dt><span id="caption_subject">', $txt['subject'], '</span></dt>
666
+							<dd><input type="text" name="subject" tabindex="1" size="80" maxlength="80" class="input_text" /></dd>
667
+						</dl>
668
+						<textarea class="editor" name="message" rows="12" cols="60" tabindex="2" style="width: 90%; height: 150px;"></textarea>
669
+						<br /><br />
670
+						<input type="hidden" value="', $forum_version, '" name="smf_version" />
671
+						<input type="hidden" value="', $context['shd_version'], '" name="shd_version" />
672
+						<input type="submit" value="', $txt['shd_admin_support_send'], '" tabindex="3" accesskey="s" class="button_submit" />
673
+					</form>
674
+				</div>
675
+			<span class="lowerframe"><span></span></span>
676
+		</div>
677
+	</div>
678
+	<div class="shd_admin_rightcolumn floatleft">
679
+		<div class="tborder">
680
+			<div class="title_bar grid_header">
681
+				<h3 class="titlebg sd_no_margin">
682
+					<img src="', $settings['images_url'], '/helptopics.gif" alt="?" /> ', $txt['shd_admin_support_what_is_this'], '
683
+				</h3>
684
+			</div>
685
+			<div class="windowbg2">
686
+				<div class="content smalltext">
687
+					', $txt['shd_admin_support_explanation'], '
688
+				</div>
689
+				<span class="botslice"><span></span></span>
690
+			</div>
691
+		</div>
692
+	</div>
693
+	<br class="clear" />';
694
+}
695
+
696
+?>
0 697
\ No newline at end of file
... ...
@@ -0,0 +1,81 @@
1
+<?php
2
+// Version: 1.0 Felidae; SimpleDesk ticket assignment
3
+
4
+/**
5
+ *	Handles ticket assignment.
6
+ *
7
+ *	@package template
8
+ *	@since 1.0
9
+*/
10
+
11
+/**
12
+ *	Displays the list of possible users a ticket can have assigned.
13
+ *
14
+ *	Will have been populated by shd_assign() in SimpleDesk-Assign.php, adding into $context['member_list'].
15
+ *
16
+ *	This allows users to assign tickets to other users, or themselves, or to unassign a previously assigned ticket. Future versions will
17
+ *	likely add further options here.
18
+ *
19
+ *	@see shd_assign()
20
+ *	@since 1.0
21
+*/
22
+function template_assign()
23
+{
24
+	global $context, $txt, $scripturl, $settings;
25
+
26
+	if (empty($context['shd_return_to']))
27
+		$context['shd_return_to'] = 'ticket';
28
+
29
+	// Back to the helpdesk.
30
+	echo '
31
+		<div class="floatleft">
32
+			', template_button_strip(array($context['navigation']['back']), 'bottom'), '
33
+		</div><br class="clear" /><br />';
34
+
35
+	echo '
36
+	<div class="cat_bar grid_header">
37
+		<h3 class="catbg">
38
+			<img src="', $settings['default_images_url'], '/simpledesk/assign.png" alt="*" />
39
+			', $txt['shd_ticket_assign_ticket'], '
40
+		</h3>
41
+	</div>
42
+	<div class="roundframe">
43
+		<form action="', $scripturl, '?action=helpdesk;sa=assign2;ticket=', $context['ticket_id'], '" method="post" onsubmit="submitonce(this);">
44
+			<div class="content">
45
+				<dl class="settings">
46
+					<dt>
47
+						<strong>', $txt['shd_ticket_assignedto'], ':</strong>
48
+					</dt>
49
+					<dd>
50
+						', $context['member_list'][$context['ticket_assigned']], '
51
+					</dd>
52
+					<dt>
53
+						<strong>', $txt['shd_ticket_assign_to'], ':</strong>
54
+					</dt>
55
+					<dd>
56
+						<select name="to_user">';
57
+
58
+	foreach ($context['member_list'] as $id => $name)
59
+		echo '
60
+							<option value="', $id, '"', ($id == $context['ticket_assigned'] ? ' selected="selected"' : ''), '>', $name, '</option>';
61
+
62
+	echo '
63
+						</select>
64
+					</dd>
65
+				</dl>
66
+				<input type="submit" value="', $txt['shd_ticket_assign_ticket'], '" onclick="return submitThisOnce(this);" accesskey="s" class="button_submit" />
67
+				<input type="submit" name="cancel" value="', ($context['shd_return_to'] == 'home' ? $txt['shd_cancel_home'] : $txt['shd_cancel_ticket']), '" accesskey="c" class="button_submit" />
68
+				<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />';
69
+
70
+	if ($context['shd_return_to'] == 'home')
71
+		echo '
72
+				<input type="hidden" name="home" value="1" />';
73
+
74
+	echo '
75
+			</div>
76
+		</form>
77
+	</div>
78
+	<span class="lowerframe"><span></span></span>';
79
+}
80
+
81
+?>
0 82
\ No newline at end of file
... ...
@@ -0,0 +1,374 @@
1
+<?php
2
+// Version: 1.0 Felidae; SimpleDesk ticket display
3
+
4
+/**
5
+ *	This file handles just displaying a ticket, its replies and working with SimpleDesk-Post.template.php to arrange the quick reply area.
6
+ *
7
+ *	@package template
8
+ *	@since 1.0
9
+*/
10
+
11
+/**
12
+ *	Display the main view of a ticket.
13
+ *
14
+ *	It is responsible for all the processing and display of the data gathered in {@link shd_view_ticket()} to the user, and in fact there
15
+ *	is little to discuss other than simply displaying the ticket in given HTML.
16
+ *
17
+ *	It is also responsible for displaying attachments either to the ticket (in attachments-in-ticket mode) or to the first post
18
+ *	(in attachments-in-replies mode), and calling upon the posting routines to set up display of 'advanced' mode in quick reply.
19
+ *
20
+ *	@see shd_view_ticket()
21
+ *	@see template_ticket_postbox()
22
+ *	@see template_ticket_meta()
23
+ *	@since 1.0
24
+*/
25
+function template_viewticket()
26
+{
27
+	global $context, $txt, $scripturl, $settings, $modSettings, $options;
28
+
29
+	// Back to the helpdesk.
30
+	echo'<div class="floatleft">', template_button_strip(array($context['navigation']['back']), 'bottom'), '</div>', ($modSettings['shd_ticketnav_style'] != 'smf' ? '<br class="clear" /><br />' : ''), '';
31
+
32
+	if ($modSettings['shd_ticketnav_style'] == 'smf')
33
+	{
34
+		template_button_strip($context['ticket_navigation'], 'right');
35
+		echo '<br class="clear" />';
36
+	}
37
+
38
+	echo '
39
+			<div class="tborder">
40
+			<div class="cat_bar grid_header">
41
+				<h3 class="catbg ticketheader">
42
+					<span class="floatright smalltext shd_ticketlinks" id="ticket">';
43
+
44
+	if ($modSettings['shd_ticketnav_style'] == 'sd')
45
+	{
46
+		foreach ($context['ticket_navigation'] as $button)
47
+			if (!empty($button['display']))
48
+				echo '
49
+						<a href="', $button['url'], '"', (!empty($button['is_last']) ? ' id="last"' : ''), '', (!empty($button['onclick']) ? ' onclick="' . $button['onclick'] . '"' : ''), '><img src="', $settings['default_images_url'], '/simpledesk/', $button['icon'], '.png" alt="', $button['alt'], '" title="', $txt[$button['text']], '" /> ', $txt[$button['text']], '</a>';
50
+	}
51
+	elseif ($modSettings['shd_ticketnav_style'] == 'sdcompact')
52
+	{
53
+		foreach ($context['ticket_navigation'] as $button)
54
+			if (!empty($button['display']))
55
+				echo '
56
+						<a href="', $button['url'], '"', (!empty($button['is_last']) ? ' id="last"' : ''), '', (!empty($button['onclick']) ? ' onclick="' . $button['onclick'] . '"' : ''), '><img src="', $settings['default_images_url'], '/simpledesk/', $button['icon'], '.png" alt="', $button['alt'], '" title="', $txt[$button['text']], '" /></a>';
57
+	}
58
+
59
+	echo '
60
+					</span>
61
+					<img src="', $settings['default_images_url'], '/simpledesk/ticket.png" alt="x" /> ', $context['ticket']['subject'], ' [', $context['ticket']['display_id'], ']
62
+				</h3>
63
+			</div>
64
+			<div class="windowbg">
65
+				<div class="content shd_ticket">';
66
+
67
+			// General ticket details
68
+			echo '
69
+					<div class="information shd_ticketdetails">
70
+						<strong><img src="', $settings['default_images_url'], '/simpledesk/details.png" alt="" class="shd_smallicon shd_icon_minihead" /> ', $txt['shd_ticket_details'], '</strong>
71
+						<hr />
72
+						<ul>
73
+							<li><img src="', $settings['default_images_url'], '/simpledesk/id.png" alt="" class="shd_smallicon" /> ', $txt['shd_ticket_id'], ': ', $context['ticket']['display_id'], '</li>
74
+							<li><img src="', $settings['default_images_url'], '/simpledesk/user.png" alt="" class="shd_smallicon" /> ', $txt['shd_ticket_user'], ': ', $context['ticket']['member']['link'], '</li>
75
+							<li><img src="', $settings['default_images_url'], '/simpledesk/time.png" alt="" class="shd_smallicon" /> ', $txt['shd_ticket_date'], ': ', $context['ticket']['poster_time'], '</li>
76
+							<li>
77
+								<img src="', $settings['default_images_url'], '/simpledesk/urgency.png" alt="" class="shd_smallicon" />
78
+								', $txt['shd_ticket_urgency'], ': <span id="urgency">', $context['ticket']['urgency']['label'], '</span>
79
+									<span id="urgency_increase">', (!empty($context['ticket']['urgency']['increase']) ? '<a id="urglink_increase" href="' . $scripturl . '?action=helpdesk;sa=urgencychange;ticket=' . $context['ticket']['id'] . ';change=increase;' . $context['session_var'] . '=' . $context['session_id'] . '" title="' . $txt['shd_urgency_increase'] . '"><img src="' . $settings['images_url'] . '/sort_up.gif" width="9px" alt="' . $txt['shd_urgency_increase'] . '" /></a>' : ''), '</span>
80
+									<span id="urgency_decrease">', (!empty($context['ticket']['urgency']['decrease']) ? '<a id="urglink_decrease" href="' . $scripturl . '?action=helpdesk;sa=urgencychange;ticket=' . $context['ticket']['id'] . ';change=decrease;' . $context['session_var'] . '=' . $context['session_id'] . '" title="' . $txt['shd_urgency_decrease'] . '"><img src="' . $settings['images_url'] . '/sort_down.gif" width="9px" alt="' . $txt['shd_urgency_decrease'] . '" /></a>' : ''), '</span>
81
+							</li>
82
+							<li><img src="', $settings['default_images_url'], '/simpledesk/staff.png" alt="" class="shd_smallicon" /> ', $txt['shd_ticket_assignedto'], ': ', $context['ticket']['assigned']['link'], '</li>
83
+							<li><img src="', $settings['default_images_url'], '/simpledesk/status.png" alt="" class="shd_smallicon"/> ', $txt['shd_ticket_status'], ': ', $context['ticket']['status']['label'], '</li>
84
+							<li><img src="', $settings['default_images_url'], '/simpledesk/replies.png" alt="" class="shd_smallicon" /> ', $txt['shd_ticket_num_replies'], ': <a href="#replies">', (empty($context['ticket']['display_recycle']) ? $context['ticket']['num_replies'] : (int) $context['ticket']['num_replies'] + (int) $context['ticket']['deleted_replies']), '</a></li>';
85
+
86
+			if (!empty($context['display_private']))
87
+				echo '
88
+							<li><img src="', $settings['default_images_url'], '/simpledesk/private.png" alt="" class="shd_smallicon" /> ', $txt['shd_ticket_privacy'], ': <span id="privacy">', $context['ticket']['privacy']['label'], '</span>', ($context['ticket']['privacy']['can_change'] ? ' (<a id="privlink" href="' . $scripturl . '?action=helpdesk;sa=privacychange;ticket=' . $context['ticket']['id'] . ';' . $context['session_var'] . '=' . $context['session_id'] . '">' . $txt['shd_ticket_change'] . '</a>)' : ''), '</li>';
89
+
90
+			if (!empty($context['can_see_ip']) && !empty($context['ticket']['ip_address']))
91
+				echo '
92
+							<li><img src="', $settings['default_images_url'], '/simpledesk/ip.png" alt="" class="shd_smallicon" /> ', $txt['shd_ticket_ip'], ': ', $context['ticket']['ip_address'], '</li>';
93
+
94
+			echo '
95
+						</ul>';
96
+
97
+			// Display ticket poster avatar?
98
+			if (!empty($modSettings['shd_display_avatar']) && empty($options['show_no_avatars']) && !empty($context['ticket']['poster_avatar']['image']))
99
+				echo '
100
+						<div class="shd_ticket_avatar">
101
+							', $context['ticket']['poster_avatar']['image'], '
102
+						</div>';
103
+
104
+			echo '
105
+					</div>';
106
+
107
+			echo '
108
+					<div class="shd_ticket_description">';
109
+
110
+			if (!empty($context['ticket']['display_recycle']))
111
+				echo '
112
+						<div class="errorbox" id="recycle_warning">
113
+							<img src="', $settings['default_images_url'], '/simpledesk/delete.png" alt="" /> ', $context['ticket']['display_recycle'], '
114
+						</div>';
115
+
116
+			echo '
117
+						<img src="', $settings['default_images_url'], '/simpledesk/name.png" alt="" class="shd_smallicon shd_icon_minihead" /> <strong>', $context['ticket']['subject'], '</strong><hr /><br />
118
+							<div id="shd_ticket_text">
119
+								', $context['ticket']['body'];
120
+
121
+			if ($settings['show_modify'] && !empty($context['ticket']['modified']))
122
+			{
123
+				echo '
124
+							<div class="smalltext shd_modified">
125
+								&#171; <em>', $txt['last_edit'], ': ', $context['ticket']['modified']['time'], ' ', $txt['by'], ' ', $context['ticket']['modified']['link'], '</em> &#187;
126
+							</div>';
127
+			}
128
+
129
+			echo'
130
+						</div>';
131
+
132
+			if ($context['can_reply'])
133
+				echo '
134
+						<br />
135
+						<div class="description shd_replybutton floatright" id="shd_replybutton">
136
+							<a href="', $scripturl, '?action=helpdesk;sa=reply;ticket=', $context['ticket_id'], ';num_replies=', $context['ticket']['num_replies'], ';', $context['session_var'], '=', $context['session_id'], '">', $txt['shd_ticket_reply'], '</a><br />
137
+						</div>';
138
+
139
+			if ($context['can_quote'])
140
+				echo '
141
+						<div class="description shd_quotebutton floatright" id="shd_quotebutton">
142
+							<a onclick="return oQuickReply.quote(', $context['ticket']['first_msg'], ', \'', $context['session_id'], '\', \'', $context['session_var'], '\', true);" href="', $scripturl, '?action=helpdesk;sa=reply;ticket=', $context['ticket_id'], ';quote=', $context['ticket']['first_msg'], ';num_replies=', $context['ticket']['num_replies'], ';', $context['session_var'], '=', $context['session_id'], '">', $txt['shd_ticket_quote'], '</a><br />
143
+						</div>';
144
+
145
+			if (!empty($context['ticket_attach']['reply'][$context['ticket']['first_msg']]))
146
+			{
147
+				echo '
148
+						<div class="information shd_reply_attachments">
149
+							<dl id="postAttachment">
150
+								<dt>
151
+									', $txt['attached'], ':
152
+								</dt>';
153
+				foreach ($context['ticket_attach']['reply'][$context['ticket']['first_msg']] as $attachment)
154
+					echo '
155
+								<dd class="smalltext">
156
+									', $attachment['link'], '
157
+								</dd>';
158
+				echo '
159
+							</dl>
160
+						</div>';
161
+			}
162
+
163
+			echo '
164
+					</div>
165
+				</div>
166
+				<span class="botslice"><span></span></span>
167
+			</div>
168
+			</div>
169
+			<br />';
170
+
171
+		// The attachments column
172
+		if (!empty($context['ticket_attach']['ticket']))
173
+		{
174
+			echo '
175
+					<div class="shd_ticket_leftcolumn floatleft">
176
+						<div class="tborder shd_attachmentcolumn">
177
+						<div class="title_bar grid_header">
178
+							<h3 class="titlebg">
179
+								<img src="', $settings['default_images_url'], '/simpledesk/attachments.png" alt="x" />', $txt['shd_ticket_attachments'], ' (', count($context['ticket_attach']['ticket']), ')
180
+							</h3>
181
+						</div>
182
+						<div class="windowbg2">
183
+							<div class="shd_attachmentbox">';
184
+
185
+			foreach ($context['ticket_attach']['ticket'] as $attachment)
186
+			{
187
+				echo '
188
+								<div class="description shd_attachment" id="attach', $attachment['id'], '">
189
+									<strong>', $attachment['link'], '</strong>
190
+									<span class="smalltext">
191
+										(', $attachment['size'], ')
192
+									</span>
193
+								</div>';
194
+			}
195
+
196
+			echo '
197
+							</div>
198
+							<span class="botslice"><span></span></span>
199
+						</div>
200
+						</div>
201
+					</div>';
202
+		}
203
+
204
+		// The replies column
205
+		echo '
206
+					<div class="shd_ticket_rightcolumn floatleft"', empty($context['ticket_attach']['ticket']) ? ' style="width: 100%;"' : '', '>', template_viewreplies(), '</div><br class="clear" />';
207
+
208
+		// Our mighty quick reply box :D
209
+		if ($context['can_reply'] && !empty($options['display_quick_reply']))
210
+		{
211
+			echo '
212
+			<br />
213
+			<div class="tborder">
214
+				<div class="title_bar', $options['display_quick_reply'] == 2 ? ' grid_header' : '', '" id="quickreplyheader" >
215
+					<h3 class="titlebg">
216
+						<span class="floatright"><a href="javascript:oQuickReply.swap();"><img src="', $settings['images_url'], '/', $options['display_quick_reply'] == 2 ? 'collapse' : 'expand', '.gif" alt="+" id="quickReplyExpand" class="icon" /></a></span>
217
+						<img src="', $settings['default_images_url'], '/simpledesk/respond.png" alt="x" />
218
+						<a href="javascript:oQuickReply.swap();">', $txt['shd_reply_ticket'], '</a>
219
+					</h3>
220
+				</div>
221
+				<div class="roundframe" id="quickReplyOptions"', $options['display_quick_reply'] == 2 ? '' : ' style="display: none"', '>
222
+					<div class="content">
223
+						<form action="', $scripturl, '?action=helpdesk;sa=savereply" method="post" accept-charset="', $context['character_set'], '" name="postreply" id="postreply" onsubmit="submitonce(this);smc_saveEntities(\'postreply\', [\'shd_reply\'], \'options\');" enctype="multipart/form-data" style="margin: 0;">';
224
+
225
+			if ($context['can_go_advanced'])
226
+			{
227
+				echo '
228
+						<div class="description shd_advancedbutton floatright" id="shd_goadvancedbutton">
229
+							<a onclick="goAdvanced(); return false;" href="', $scripturl, '?action=helpdesk;sa=reply;ticket=', $context['ticket_id'], ';', $context['session_var'], '=', $context['session_id'], '">', $txt['shd_go_advanced'], '</a><br />
230
+						</div>';
231
+			}
232
+
233
+			template_ticket_postbox();
234
+			template_ticket_meta();
235
+
236
+			echo '
237
+						</form>
238
+					</div>
239
+				</div>
240
+				<span id="quickreplyfooter" class="lowerframe"', $options['display_quick_reply'] == 2 ? '' : ' style="display: none"', '><span></span></span>
241
+			</div>';
242
+		}
243
+
244
+}
245
+
246
+/**
247
+ *	Display all the replies to a ticket.
248
+ *
249
+ *	This function deals simply with viewing of replies in a ticket, including deleted replies, which is initialised in {@link shd_view_ticket()}
250
+ *	and data gathered through the {@link shd_prepare_ticket_context()} call back, which simply deals with a single reply at a time.
251
+ *
252
+ *	@see shd_view_ticket()
253
+ *	@see templaet_view_ticket()
254
+ *	@since 1.0
255
+*/
256
+function template_viewreplies()
257
+{
258
+	global $context, $settings, $txt, $scripturl, $options, $modSettings, $reply_request;
259
+
260
+	echo '
261
+		<div class="tborder">
262
+		<div class="title_bar grid_header">
263
+			<h3 class="titlebg">
264
+				<span class="floatright smalltext">', $txt['pages'], ': ', $context['page_index'], '</span>
265
+				<img src="', $settings['default_images_url'], '/simpledesk/replies.png" alt="x" /> ', $txt['shd_ticket_replies'], '
266
+			</h3>
267
+		</div>
268
+		<div class="roundframe" id="replies">
269
+			<div class="content">';
270
+
271
+	if (empty($reply_request))
272
+	{
273
+		echo $txt['shd_no_replies'];
274
+	}
275
+	else
276
+	{
277
+		while ($reply = $context['get_replies']())
278
+		{
279
+			echo '
280
+					<div class="description shd_reply', (!empty($context['ticket']['display_recycle']) && $reply['message_status'] == MSG_STATUS_DELETED ? ' errorbox' : ''), '" id="msg', $reply['id'], '">
281
+						<span class="floatleft shd_posterinfo">
282
+							<strong class="shd_postername">
283
+								', $reply['member']['link'], '
284
+							</strong>
285
+							<br />
286
+							', $reply['member']['group'], '<br class="shd_groupmargin" />';
287
+
288
+			if (!empty($modSettings['shd_display_avatar']) && empty($options['show_no_avatars']) && !empty($reply['member']['avatar']['image']))
289
+					echo '
290
+							', $reply['member']['avatar']['image'];
291
+
292
+			if ($modSettings['shd_staff_badge'] == (!empty($reply['is_team']) ? 'staffbadge' : 'userbadge') || $modSettings['shd_staff_badge'] == 'bothbadge')
293
+				echo '<br />
294
+							', $reply['member']['group_stars'];
295
+			elseif (!empty($reply['is_team']) && $modSettings['shd_staff_badge'] == 'nobadge')
296
+				echo '<br />
297
+							<img src="', $settings['default_images_url'] . '/simpledesk/staff.png" class="shd_smallicon" title="', $txt['shd_ticket_staff'], '" alt="', $txt['shd_ticket_staff'], '" />';
298
+
299
+			echo '
300
+						</span>
301
+						<div class="shd_replyarea">
302
+							<div class="smalltext">
303
+								<span class="floatright shd_ticketlinks">';
304
+			if ($context['can_quote'])
305
+				echo '
306
+									<img src="', $settings['default_images_url'], '/simpledesk/quote.png" class="shd_smallicon" alt="*" /><a onclick="return oQuickReply.quote(', $reply['id'], ', \'', $context['session_id'], '\', \'', $context['session_var'], '\', true);" href="', $scripturl, '?action=helpdesk;sa=reply;ticket=', $context['ticket_id'], ';quote=', $reply['id'], ';', $context['session_var'], '=', $context['session_id'], '">', $txt['shd_ticket_quote_short'], '</a>';
307
+			if ($reply['can_edit'])
308
+				echo '
309
+									<img src="', $settings['default_images_url'], '/simpledesk/edit.png" class="shd_smallicon" alt="*" /><a href="', $scripturl, '?action=helpdesk;sa=editreply;ticket=', $context['ticket_id'], ';msg=', $reply['id'], ';', $context['session_var'], '=', $context['session_id'], '">', $txt['shd_ticket_edit'], '</a>';
310
+			if ($reply['can_delete'])
311
+				echo '
312
+									<img src="', $settings['default_images_url'], '/simpledesk/delete.png" class="shd_smallicon" alt="*" /><a href="', $scripturl, '?action=helpdesk;sa=deletereply;reply=', $reply['id'], ';ticket=', $context['ticket']['id'], ';', $context['session_var'], '=', $context['session_id'], '" onclick="return confirm(', JavaScriptEscape($txt['shd_delete_reply_confirm']), ');">', $txt['shd_ticket_delete'], '</a>';
313
+			if ($reply['can_restore'])
314
+				echo '
315
+									<img src="', $settings['default_images_url'], '/simpledesk/restore.png" class="shd_smallicon" alt="*" /><a href="', $scripturl, '?action=helpdesk;sa=restorereply;reply=', $reply['id'], ';ticket=', $context['ticket']['id'], ';', $context['session_var'], '=', $context['session_id'], '">', $txt['shd_ticket_restore'], '</a>';
316
+			if ($reply['can_permadelete'])
317
+				echo '
318
+									<img src="', $settings['default_images_url'], '/simpledesk/delete.png" class="shd_smallicon" alt="*" /><a href="', $scripturl, '?action=helpdesk;sa=permadelete;reply=', $reply['id'], ';ticket=', $context['ticket']['id'], ';', $context['session_var'], '=', $context['session_id'], '" onclick="return confirm(', JavaScriptEscape($txt['shd_delete_permanently_confirm']), ');">', $txt['shd_delete_permanently'], '</a>';
319
+
320
+			echo '
321
+								</span>
322
+								', sprintf($txt['shd_reply_written'], $reply['time']), '
323
+							</div>
324
+							<hr class="clearfix" />
325
+							', $reply['body'], '
326
+							<br /><br />';
327
+
328
+			if ($settings['show_modify'] && !empty($reply['modified']))
329
+			{
330
+				echo '
331
+							<div class="smalltext shd_modified" style="margin-top:20px;">
332
+								&#171; <em>', $txt['last_edit'], ': ', $reply['modified']['time'], ' ', $txt['by'], ' ', $reply['modified']['link'], '</em> &#187;
333
+							</div>';
334
+			}
335
+
336
+			if (!empty($context['ticket_attach']['reply'][$reply['id']]))
337
+			{
338
+				echo '
339
+							<div class="smalltext">
340
+								<strong>', $txt['shd_ticket_attachments'], '</strong><br />
341
+								<ul class="shd_replyattachments">';
342
+
343
+				foreach ($context['ticket_attach']['reply'][$reply['id']] as $attach)
344
+					echo '
345
+									<li>', $attach['link'], '</li>';
346
+
347
+				echo '
348
+								</ul>
349
+							</div>';
350
+			}
351
+
352
+			echo '
353
+						</div>';
354
+
355
+			if (!empty($context['can_see_ip']) && !empty($reply['ip_address']))
356
+				echo '
357
+						<span class="floatright"><img src="', $settings['default_images_url'], '/simpledesk/ip.png" alt="" class="shd_smallicon" /> ', $txt['shd_ticket_ip'], ': ', $reply['ip_address'], '</span>';
358
+
359
+			echo '
360
+						<br class="clear" />
361
+					</div>';
362
+		}
363
+	}
364
+
365
+	echo '
366
+				</div>
367
+				<span class="floatright smalltext">', $txt['pages'], ': ', $context['page_index'], '</span>
368
+				<br class="clear" />
369
+			</div>
370
+			<span class="lowerframe"><span></span></span>
371
+		</div>';
372
+}
373
+
374
+?>
0 375
\ No newline at end of file
... ...
@@ -0,0 +1,583 @@
1
+<?php
2
+// Version: 1.0 Felidae; SimpleDesk posting and replying
3
+
4
+/**
5
+ *	This file handles everything concerning posting, including displaying the display of ticket facia
6
+ *	around a post box, display of replies with insert-quote links, the postbox, attachments, errors etc.
7
+ *
8
+ *	@package template
9
+ *	@todo Finish documenting this file.
10
+ *	@since 1.0
11
+*/
12
+
13
+/**
14
+ *	Entry point for displaying the post new ticket/edit ticket UI.
15
+ *
16
+ *	@since 1.0
17
+*/
18
+
19
+function template_ticket_post()
20
+{
21
+	global $context;
22
+
23
+	// Back to the helpdesk.
24
+	echo '
25
+		<div class="floatleft">
26
+			', template_button_strip(array($context['navigation']['back']), 'bottom'), '
27
+		</div><br class="clear" /><br />';
28
+
29
+	template_preview();
30
+	template_ticket_info();
31
+	template_ticket_subjectbox();
32
+	template_ticket_meta();
33
+	template_ticket_postbox();
34
+	template_ticket_footer();
35
+	if (!empty($context['ticket_form']['do_replies']))
36
+	{
37
+		template_ticket_begin_replies();
38
+		template_ticket_do_replies();
39
+		template_ticket_end_replies();
40
+	}
41
+	template_ticket_pageend();
42
+}
43
+
44
+function template_reply_post()
45
+{
46
+	global $context;
47
+
48
+	template_ticket_info();
49
+	template_ticket_content();
50
+	template_ticket_meta();
51
+	template_ticket_footer();
52
+	template_preview();
53
+	template_ticket_shd_replyarea();
54
+	if (!empty($context['ticket_form']['do_replies']))
55
+	{
56
+		template_ticket_begin_replies();
57
+		template_ticket_do_replies();
58
+		template_ticket_end_replies();
59
+	}
60
+	template_ticket_pageend();
61
+}
62
+
63
+// yes, this isn't strictly conventional SMF style
64
+function template_ticket_option($option)
65
+{
66
+	global $context, $txt;
67
+
68
+	if (!empty($context['ticket_form'][$option]['can_change']))
69
+	{
70
+		echo '
71
+								<select name="shd_' . $option . '">';
72
+		foreach ($context['ticket_form'][$option]['options'] as $value => $caption)
73
+		{
74
+			echo '
75
+									<option value="', $value, '"', ($value == $context['ticket_form'][$option]['setting'] ? ' selected="selected"' : ''), '>', $txt[$caption], '</option>';
76
+		}
77
+		echo '
78
+								</select>';
79
+	}
80
+	else
81
+		echo $txt[$context['ticket_form'][$option]['options'][$context['ticket_form'][$option]['setting']]];
82
+}
83
+
84
+function template_ticket_info()
85
+{
86
+	global $context, $txt, $scripturl, $settings, $modSettings, $options;
87
+
88
+	echo '
89
+			<form action="', $context['ticket_form']['form_action'], '" method="post" accept-charset="', $context['character_set'], '" name="postmodify" id="postmodify" onsubmit="', 'submitonce(this);smc_saveEntities(\'postmodify\', [\'subject\', \'', $context['post_box_name'], '\'], \'options\');" enctype="multipart/form-data" style="margin: 0;">
90
+			<div class="tborder">
91
+			<div class="cat_bar grid_header">
92
+				<h3 class="catbg">
93
+					<img src="', $settings['default_images_url'], '/simpledesk/ticket.png" alt="x" /> ', $context['ticket_form']['form_title'], '
94
+				</h3>
95
+			</div>
96
+			<div class="windowbg">
97
+				<div class="content shd_ticket">';
98
+
99
+	// General ticket details
100
+	echo '
101
+					<div class="information shd_ticketdetails">
102
+						<strong><img src="', $settings['default_images_url'], '/simpledesk/details.png" alt="" class="shd_smallicon" /> ', $txt['shd_ticket_details'], '</strong>
103
+						<hr />
104
+						<ul>';
105
+
106
+	if (!empty($context['ticket_form']['display_id']))
107
+		echo '
108
+							<li><img src="', $settings['default_images_url'], '/simpledesk/id.png" alt="" class="shd_smallicon" /> ', $txt['shd_ticket_id'], ': ', $context['ticket_form']['display_id'], '</li>';
109
+
110
+	if (!empty($context['ticket_form']['member']['link']))
111
+		echo '
112
+							<li><img src="', $settings['default_images_url'], '/simpledesk/user.png" alt="" class="shd_smallicon" /> ', $txt['shd_ticket_user'], ': ', $context['ticket_form']['member']['link'], '</li>';
113
+
114
+	echo '
115
+							<li>
116
+								<img src="', $settings['default_images_url'], '/simpledesk/urgency.png" alt="" class="shd_smallicon" />
117
+								', $txt['shd_ticket_urgency'], ': ', template_ticket_option('urgency'), '
118
+							</li>';
119
+
120
+	// New tickets aren't assigned - ever - but existing ones might be
121
+	if (!empty($context['ticket_form']['ticket']))
122
+		echo '
123
+							<li><img src="', $settings['default_images_url'], '/simpledesk/staff.png" alt="" class="shd_smallicon" /> ', $txt['shd_ticket_assignedto'], ': ', !empty($context['ticket_form']['assigned']['link']) ? $context['ticket_form']['assigned']['link'] : '<span class="error">' . $txt['shd_unassigned'] . '</span>', '</li>';
124
+
125
+	echo '
126
+							<li><img src="', $settings['default_images_url'], '/simpledesk/status.png" alt="" class="shd_smallicon"/> ', $txt['shd_ticket_status'], ': ', $txt['shd_status_' . $context['ticket_form']['status']], '</li>';
127
+
128
+	if (!empty($context['display_private']))
129
+		echo '
130
+							<li>
131
+								<img src="', $settings['default_images_url'], '/simpledesk/private.png" alt="" class="shd_smallicon" />
132
+								', $txt['shd_ticket_privacy'], ': ', template_ticket_option('private'), '
133
+							</li>
134
+						</ul>';
135
+
136
+	// Display ticket poster avatar?
137
+	if (!empty($modSettings['shd_display_avatar']) && empty($options['show_no_avatars']) && !empty($context['ticket_form']['member']['avatar']['image']))
138
+		echo '
139
+						<div class="shd_ticket_avatar">
140
+							', $context['ticket_form']['member']['avatar']['image'], '
141
+						</div>';
142
+
143
+	echo '
144
+					</div>';
145
+}
146
+
147
+function template_ticket_posterrors()
148
+{
149
+	global $context, $txt;
150
+
151
+	// Did anything go wrong?
152
+	if (!empty($context['shd_errors']))
153
+	{
154
+		echo '
155
+						<div class="errorbox" id="errors">
156
+							<dl>
157
+								<dt>
158
+									<strong style="" id="error_serious">', $txt['shd_ticket_post_error'], ':</strong>
159
+								</dt>
160
+								<dt class="error" id="error_list">';
161
+
162
+		foreach ($context['shd_errors'] as $error)
163
+			echo '
164
+									', $txt['error_' . $error], '<br />';
165
+
166
+		echo '
167
+								</dt>
168
+							</dl>
169
+						</div>';
170
+	}
171
+}
172
+
173
+function template_ticket_subjectbox()
174
+{
175
+	global $context, $txt, $scripturl, $settings, $modSettings;
176
+
177
+	echo '
178
+					<div class="shd_ticket_description">';
179
+
180
+	template_ticket_posterrors();
181
+	echo '
182
+						<img src="', $settings['default_images_url'], '/simpledesk/description.png" alt="" class="shd_smallicon" /> <strong>', $txt['shd_ticket_subject'], ':</strong>
183
+						<input type="text" name="subject" size="50" maxlength="100" class="input_text" value="', $context['ticket_form']['subject'], '" tabindex="', $context['tabindex']++, '" />
184
+						<hr /><br />
185
+							';
186
+}
187
+
188
+function template_ticket_content()
189
+{
190
+	global $context, $txt, $scripturl, $settings, $modSettings;
191
+
192
+	echo '
193
+					<div class="shd_ticket_description">';
194
+
195
+	template_ticket_posterrors();
196
+	echo '
197
+						<img src="', $settings['default_images_url'], '/simpledesk/description.png" alt="" class="shd_smallicon" /> <strong>', $context['ticket_form']['subject'], '</strong>
198
+						<hr /><br />
199
+							', $context['ticket_form']['message'];
200
+
201
+	if ($settings['show_modify'] && !empty($context['ticket']['modified']))
202
+	{
203
+		echo '
204
+						<div class="smalltext shd_modified" style="margin-top:20px;">
205
+							&#171; <em>', $txt['last_edit'], ': ', $context['ticket']['modified']['time'], ' ', $txt['by'], ' ', $context['ticket']['modified']['link'], '</em> &#187;
206
+						</div>';
207
+	}
208
+}
209
+
210
+function template_ticket_meta()
211
+{
212
+	global $context;
213
+
214
+	// Management/meta information
215
+	echo '
216
+						<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
217
+						<input type="hidden" name="seqnum" value="', $context['form_sequence_number'], '" />';
218
+
219
+	if (!empty($context['ticket_form']['ticket']))
220
+		echo '
221
+						<input type="hidden" name="ticket" value="', $context['ticket_form']['ticket'], '" />';
222
+
223
+	if (!empty($context['ticket_form']['msg']))
224
+		echo '
225
+						<input type="hidden" name="msg" value="', $context['ticket_form']['msg'], '" />';
226
+
227
+	if (!empty($context['ticket_form']['num_replies']))
228
+		echo '
229
+						<input type="hidden" name="num_replies" value="', $context['ticket_form']['num_replies'], '" />';
230
+}
231
+
232
+function template_ticket_shd_replyarea()
233
+{
234
+	global $context, $settings, $scripturl, $txt;
235
+	echo '
236
+			<div class="tborder">
237
+			<div class="title_bar grid_header">
238
+				<h3 class="titlebg">
239
+					<img src="', $settings['default_images_url'], '/simpledesk/respond.png" alt="x" />
240
+					', !empty($context['ticket_form']['form_title']) ? $context['ticket_form']['form_title'] : $txt['shd_reply_ticket'], '
241
+				</h3>
242
+			</div>
243
+			<div class="roundframe">
244
+				<div class="content">';
245
+
246
+	template_ticket_postbox();
247
+
248
+	echo '
249
+				</div>
250
+			</div>
251
+			<span class="lowerframe"><span></span></span>
252
+			</div>
253
+			<br />';
254
+}
255
+
256
+function template_ticket_postbox()
257
+{
258
+	global $modSettings, $context, $txt;
259
+	$editor_context = &$context['controls']['richedit'][$context['post_box_name']];
260
+
261
+	// The postbox
262
+	echo '
263
+						<div id="shd_bbcbox"', ((empty($modSettings['shd_allow_ticket_bbc']) || !empty($context['shd_display'])) ? ' style="display:none;"' : ''), '></div>
264
+						<div id="shd_smileybox"', ((empty($modSettings['shd_allow_ticket_smileys']) || !empty($context['shd_display'])) ? ' style="display:none;"' : ''), '></div>';
265
+
266
+	echo template_control_richedit($context['post_box_name'], 'shd_smileybox', 'shd_bbcbox');
267
+
268
+	template_ticket_attachments();
269
+
270
+	echo '
271
+						<br class="clear" />
272
+						<span class="smalltext"><br />', $context['browser']['is_firefox'] ? $txt['shortcuts_firefox'] : $txt['shortcuts'], '</span><br />
273
+						<input type="submit" value="', isset($editor_context['labels']['post_button']) ? $editor_context['labels']['post_button'] : $txt['post'], '" tabindex="', $context['tabindex']++, '" accesskey="s" class="button_submit" />
274
+						<input class="button_submit" type="submit" name="preview" value="', $txt['preview'], '" accesskey="p" tabindex="', $context['tabindex']++, '" />';
275
+
276
+	template_ticket_additional_options();
277
+}
278
+
279
+function template_ticket_footer()
280
+{
281
+	global $settings, $context, $txt;
282
+
283
+	if ($settings['show_modify'] && !empty($context['ticket_form']['modified']))
284
+	{
285
+		echo '
286
+						<div class="smalltext shd_modified" style="margin-top:20px;">
287
+							&#171; <em>', $txt['last_edit'], ': ', $context['ticket_form']['modified']['time'], ' ', $txt['by'], ' ', $context['ticket_form']['modified']['link'], '</em> &#187;
288
+						</div>';
289
+	}
290
+
291
+	echo '
292
+					</div>
293
+				</div>
294
+				<span class="botslice"><span></span></span>
295
+			</div>
296
+			</div>
297
+			<br />';
298
+}
299
+
300
+function template_preview()
301
+{
302
+	global $context, $txt, $settings;
303
+
304
+	if (!empty($context['ticket_form']['preview']))
305
+	{
306
+		echo '
307
+			<div class="tborder">
308
+			<div class="title_bar grid_header">
309
+				<h3 class="titlebg">
310
+					<img src="', $settings['default_images_url'], '/simpledesk/preview.png" alt="x" />
311
+					', !empty($context['ticket_form']['preview']['title']) ? $context['ticket_form']['preview']['title'] : $txt['preview'], '
312
+				</h3>
313
+			</div>
314
+			<div class="roundframe">
315
+				<div class="content">
316
+					', $context['ticket_form']['preview']['body'], '
317
+				</div>
318
+			</div>
319
+			<span class="lowerframe"><span></span></span>
320
+			</div>
321
+			<br />';
322
+	}
323
+}
324
+
325
+function template_ticket_attachments()
326
+{
327
+	global $context, $modSettings, $txt;
328
+	if (empty($context['current_attachments']) && empty($context['ticket_form']['do_attach']))
329
+		return;
330
+
331
+	echo '
332
+					<div class="information shd_reply_attachments" id="shd_attach_container"', !empty($context['shd_display']) ? ' style="display:none;"' : '', '>';
333
+
334
+	if (!empty($context['current_attachments']))
335
+	{
336
+		echo '
337
+						<dl id="postAttachment">
338
+							<dt>
339
+								', $txt['attached'], ':
340
+							</dt>
341
+							<dd class="smalltext">
342
+								<input type="hidden" name="attach_del[]" value="0" />
343
+								', $txt['uncheck_unwatchd_attach'], ':
344
+							</dd>';
345
+		foreach ($context['current_attachments'] as $attachment)
346
+			echo '
347
+							<dd class="smalltext">
348
+								<label for="attachment_', $attachment['id'], '"><input type="checkbox" id="attachment_', $attachment['id'], '" name="attach_del[]" value="', $attachment['id'], '"', empty($attachment['unchecked']) ? ' checked="checked"' : '', ' class="input_check" onclick="javascript:oAttach.checkActive();" /> ', $attachment['name'], '</label>
349
+							</dd>';
350
+		echo '
351
+						</dl>';
352
+	}
353
+
354
+	if (!empty($context['ticket_form']['do_attach']))
355
+	{
356
+		// JS for our pretty widget
357
+		echo '
358
+						<dl id="postAttachment2">
359
+							<dt>
360
+								', $txt['attach'], ':
361
+							</dt>
362
+							<dd class="smalltext">
363
+								<input type="file" size="60" name="attachment" id="shd_attach" class="input_file" />
364
+								<div id="shd_attachlist_container"></div>
365
+							</dd>';
366
+
367
+		echo '
368
+						<dd class="smalltext">';
369
+
370
+		// Show some useful information such as allowed extensions, maximum size and amount of attachments allowed.
371
+		if (!empty($modSettings['attachmentCheckExtensions']))
372
+			echo '
373
+								', $txt['allowed_types'], ': ', $context['allowed_extensions'], '<br />';
374
+
375
+		if (!empty($context['attachment_restrictions']))
376
+			echo '
377
+								', $txt['attach_restrictions'], ' ', implode(', ', $context['attachment_restrictions']), '<br />';
378
+
379
+		echo '
380
+							</dd>
381
+						</dl>
382
+						<script type="text/javascript"><!-- // --><![CDATA[
383
+	var oAttach = new shd_attach_select({
384
+		file_item: "shd_attach",
385
+		file_container: "shd_attachlist_container",
386
+		max: ', $context['ticket_form']['num_allowed_attachments'], ',
387
+		message_txt_delete: ', JavaScriptEscape($txt['remove']);
388
+
389
+		if (!empty($modSettings['attachmentExtensions']) && !empty($modSettings['attachmentCheckExtensions']))
390
+		{
391
+			$ext = explode(',', $modSettings['attachmentExtensions']);
392
+			foreach ($ext as $k => $v)
393
+				$ext[$k] = JavaScriptEscape($v);
394
+
395
+			echo ',
396
+		message_ext_error: ', JavaScriptEscape(str_replace('{attach_exts}', $context['allowed_extensions'], $txt['shd_cannot_attach_ext'])), ',
397
+		attachment_ext: [', implode(',', $ext), ']';
398
+		}
399
+
400
+		echo '
401
+	});
402
+						// ]]></script>';
403
+	}
404
+
405
+	echo '
406
+					</div>';
407
+}
408
+
409
+function template_ticket_additional_options()
410
+{
411
+	global $context, $options, $txt, $modSettings;
412
+	echo '
413
+					<div id="postMoreOptions" class="smalltext">
414
+						<ul class="post_options">
415
+							<li><label for="check_back"><input type="checkbox" name="goback" id="check_back"', (!empty($context['ticket_form']['return_to_ticket']) || !empty($options['return_to_post']) ? ' checked="checked"' : ''), ' value="1" class="input_check" /> ', $txt['shd_back_to_ticket'], '</label></li>';
416
+
417
+	if (!empty($modSettings['shd_allow_ticket_smileys']))
418
+		echo '
419
+							<li><label for="disable_smileys"><input type="checkbox" name="no_smileys" id="disable_smileys"', (!empty($context['ticket_form']['disable_smileys']) ? ' checked="checked"' : ''), ' value="1" class="input_check" /> ', $txt['shd_disable_smileys_post'], '</label></li>';
420
+
421
+	echo '
422
+						</ul>
423
+					</div>';
424
+}
425
+
426
+function template_ticket_begin_replies()
427
+{
428
+	global $context, $settings, $txt, $modSettings;
429
+
430
+	echo '
431
+			<table width="100%">
432
+				<tr>';
433
+
434
+	// The attachments column
435
+	if (!empty($context['ticket_attach']['ticket']))
436
+	{
437
+		echo '
438
+					<td valign="top" width="25%">
439
+						<div class="tborder">
440
+						<div class="title_bar grid_header">
441
+							<h3 class="titlebg shd_attachmentcolumn">
442
+								<img src="', $settings['default_images_url'], '/simpledesk/attachments.png" alt="x" />', $txt['shd_ticket_attachments'], ' (', count($context['ticket_attach']['ticket']), ')
443
+							</h3>
444
+						</div>
445
+						<div class="windowbg2 shd_attachmentcolumn">
446
+							<div class="shd_attachmentbox">';
447
+
448
+		foreach ($context['ticket_attach']['ticket'] as $attachment)
449
+		{
450
+			echo '
451
+								<div class="description attachment" id="attach', $attachment['id'], '">
452
+									<a href="#" title="', $txt['shd_ticket_delete'], '"><img src="', $settings['default_images_url'], '/simpledesk/delete.png" class="shd_tinyicon shd_removeattachment" alt="', $txt['shd_ticket_delete'], '" /></a>
453
+									<strong><a href="#">', $attachment['filename'], '</a></strong>
454
+									<span class="smalltext">
455
+										(', $attachment['size'], ') - <a href="#ticket">', $txt['shd_ticket'], '</a>
456
+									</span>
457
+								</div>';
458
+		}
459
+
460
+		echo '
461
+							</div>
462
+							<span class="botslice"><span></span></span>
463
+						</div>
464
+						</div>
465
+					</td>';
466
+	}
467
+
468
+	// The replies column
469
+	echo '
470
+					<td valign="top">';
471
+}
472
+
473
+function template_ticket_do_replies()
474
+{
475
+	global $context, $settings, $txt, $scripturl, $options, $modSettings, $reply_request;
476
+
477
+	echo '
478
+		<div class="tborder">
479
+		<div class="title_bar grid_header">
480
+			<h3 class="titlebg">
481
+				<img src="', $settings['default_images_url'], '/simpledesk/replies.png" alt="x" /> ', $txt['shd_ticket_replies'], '
482
+			</h3>
483
+		</div>
484
+		<div class="roundframe" id="replies">
485
+			<div class="content">';
486
+
487
+	if (!empty($reply_request))
488
+	{
489
+		while ($reply = $context['get_replies']())
490
+		{
491
+			echo '
492
+					<div class="description shd_reply" id="reply', $reply['id'], '">
493
+								<span class="floatleft shd_posterinfo">
494
+									<strong class="shd_postername">
495
+										', $reply['member']['link'], '
496
+									</strong>
497
+									<br />
498
+									', $reply['member']['group'], '<br class="shd_groupmargin" />';
499
+
500
+			if (!empty($settings['show_user_images']) && empty($options['show_no_avatars']) && !empty($reply['member']['avatar']['image']))
501
+					echo '
502
+									<div>', $reply['member']['avatar']['image'], '</div>';
503
+
504
+			if (!empty($reply['is_team']))
505
+				if (empty($modSettings['shd_use_staff_badge']))
506
+					echo '<br />
507
+									<img src="', $settings['default_images_url'] . '/simpledesk/staff.png" class="shd_smallicon" title="', $txt['shd_ticket_staff'], '" alt="', $txt['shd_ticket_staff'], '" />';
508
+				else
509
+					echo '<br />
510
+									', $reply['member']['group_stars'];
511
+
512
+			echo '
513
+								</span>
514
+								<div class="shd_replyarea">
515
+									<div class="smalltext">
516
+										<span class="floatright shd_ticketlinks">';
517
+
518
+			if ($context['can_quote'])
519
+				echo '
520
+											<img src="', $settings['default_images_url'], '/simpledesk/quote.png" class="shd_smallicon" alt="*" /><a onclick="return oQuickReply.quote(', $reply['id'], ', \'', $context['session_id'], '\', \'', $context['session_var'], '\', true);" href="', $scripturl, '?action=helpdesk;sa=reply;ticket=', $context['ticket_id'], ';quote=', $reply['id'], ';', $context['session_var'], '=', $context['session_id'], '">', $txt['shd_ticket_quote_short'], '</a>';
521
+
522
+			echo '
523
+										</span>
524
+										', sprintf($txt['shd_reply_written'], $reply['time']), '
525
+									</div>
526
+									<hr class="clearfix" />
527
+									', $reply['body'], '
528
+									<br /><br />';
529
+			if ($settings['show_modify'] && !empty($reply['modified']))
530
+			{
531
+				echo '
532
+							<div class="smalltext shd_modified" style="margin-top:20px;">
533
+								&#171; <em>', $txt['last_edit'], ': ', $reply['modified']['time'], ' ', $txt['by'], ' ', $reply['modified']['link'], '</em> &#187;
534
+							</div>';
535
+			}
536
+
537
+			if (!empty($context['ticket_attach']['reply'][$reply['id']]))
538
+			{
539
+				echo '
540
+									<div class="smalltext">
541
+										<strong>', $txt['shd_ticket_attachments'], '</strong><br />
542
+										<ul class="shd_replyattachments">';
543
+
544
+				foreach ($context['ticket_attach']['reply'][$reply['id']] as $attach)
545
+					echo '
546
+											<li>', $attach['link'], '</li>';
547
+
548
+				echo '
549
+										</ul>
550
+									</div>';
551
+			}
552
+
553
+			echo '
554
+								</div>
555
+							<br class="clear" />
556
+					</div>';
557
+		}
558
+	}
559
+
560
+	echo '
561
+			</div>
562
+		</div>
563
+		<span class="lowerframe"><span></span></span>
564
+		</div>';
565
+}
566
+
567
+function template_ticket_end_replies()
568
+{
569
+	echo '</td>';
570
+
571
+	// Close the table
572
+	echo '
573
+				</tr>
574
+			</table>';
575
+}
576
+
577
+function template_ticket_pageend()
578
+{
579
+	// And finally, good night, sweet form
580
+	echo '
581
+			</form>';
582
+}
583
+?>
0 584
\ No newline at end of file
... ...
@@ -0,0 +1,168 @@
1
+<?php
2
+// Version: 1.0 Felidae; SimpleDesk ticket <-> topic template
3
+
4
+/**
5
+ *	This file handles gathering user options when moving a ticket to/from the helpdesk, from/to
6
+ *	a forum thread, specifically getting details like whether to send a PM to the user (including the PM contents), as well as if sending
7
+ *	to a board, which board.
8
+ *
9
+ *	@package template
10
+ *	@since 1.0
11
+*/
12
+
13
+/**
14
+ *	Display a list of requirements for moving a ticket to a topic.
15
+ *
16
+ *	When moving a ticket to the forum, certain information is required: the board to move to, whether to send the ticket starter a
17
+ *	personal message (and if so, the contents of the message) and what to do in the event there are deleted replies to deal with.
18
+ *	This function handles showing the form to the user.
19
+ *
20
+ *	@see shd_tickettotopic()
21
+ *	@see shd_tickettotopic2()
22
+ *
23
+ *	@since 1.0
24
+*/
25
+function template_shd_tickettotopic()
26
+{
27
+	global $txt, $settings, $context, $scripturl, $modSettings;
28
+
29
+	// Back to the helpdesk.
30
+	echo '
31
+		<div class="floatleft">
32
+			', template_button_strip(array($context['navigation']['back']), 'bottom'), '
33
+		</div><br class="clear" /><br />';
34
+
35
+	echo '
36
+		<div class="cat_bar grid_header">
37
+			<h3 class="catbg">
38
+				<img src="', $settings['default_images_url'] , '/simpledesk/tickettotopic.png" alt="*" />
39
+				', $txt['shd_move_ticket_to_topic'], '
40
+			</h3>
41
+		</div>
42
+		<div class="roundframe">
43
+		<form action="', $scripturl, '?action=helpdesk;sa=tickettotopic2;ticket=', $context['ticket_id'], '" method="post" onsubmit="submitonce(this);">
44
+			<div class="content">
45
+				<dl class="settings">
46
+					<dt>
47
+						<strong>', $txt['shd_ticket_board'], ':</strong>
48
+					</dt>
49
+					<dd>
50
+						<select name="toboard">';
51
+
52
+	foreach ($context['categories'] as $category)
53
+	{
54
+		echo '
55
+							<optgroup label="', $category['name'], '">';
56
+
57
+		foreach ($category['boards'] as $board)
58
+			echo '
59
+								<option value="', $board['id'], '"', $board['selected'] ? ' selected="selected"' : '', '>', $board['child_level'] > 0 ? str_repeat('==', $board['child_level']-1) . '=&gt; ' : '', $board['name'], '</option>';
60
+		echo '
61
+							</optgroup>';
62
+	}
63
+
64
+	echo '
65
+						</select>
66
+					</dd>
67
+					<dt>
68
+						<strong>', $txt['shd_move_send_pm'], ':</strong>
69
+					</dt>
70
+					<dd>
71
+						<input type="checkbox" name="send_pm" id="send_pm" checked="checked" onclick="document.getElementById(\'pm_message\').style.display = this.checked ? \'block\' : \'none\';" class="input_check" />
72
+					</dd>
73
+				</dl>
74
+				<fieldset id="pm_message">
75
+					<dl class="settings">
76
+						<dt>
77
+							', $txt['shd_move_why'], '
78
+						</dt>
79
+						<dd>
80
+							<textarea name="pm_content" rows="9" cols="70">', $txt['shd_move_default'], '</textarea>
81
+						</dd>
82
+					</dl>
83
+				</fieldset>';
84
+
85
+	if (!empty($context['deleted_prompt']))
86
+	{
87
+		echo '
88
+				<br />
89
+				<fieldset id="deleted_replies">
90
+					<dl class="settings">
91
+						<dt>
92
+							', $txt['shd_ticket_move_deleted'], '
93
+						</dt>
94
+						<dd>
95
+							<select name="deleted_replies">
96
+								<option value="abort">', $txt['shd_ticket_move_deleted_abort'], '</option>
97
+								<option value="delete">', $txt['shd_ticket_move_deleted_delete'], '</option>
98
+								<option value="undelete">', $txt['shd_ticket_move_deleted_undelete'], '</option>
99
+							</select>
100
+						</dd>
101
+					</dl>
102
+				</fieldset>';
103
+	}
104
+
105
+	echo '
106
+				<input type="submit" value="', $txt['shd_move_ticket'], '" onclick="return submitThisOnce(this);" accesskey="s" class="button_submit" />
107
+				<input type="submit" name="cancel" value="', $txt['shd_cancel_ticket'], '" accesskey="c" class="button_submit" />
108
+				<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
109
+				<input type="hidden" name="seqnum" value="', $context['form_sequence_number'], '" />
110
+			</div>
111
+		</form>
112
+		</div>
113
+		<span class="lowerframe"><span></span></span>';
114
+}
115
+
116
+/**
117
+ *	Display a list of requirements for moving a topic to a ticket.
118
+ *
119
+ *	When moving a ticket from the forum, only a little information is required; basically, whether to send a personal message (and what message)
120
+ *	to the topic/ticket starter or not.
121
+ *
122
+ *	@see shd_topictoticket()
123
+ *	@see shd_topictoticket2()
124
+ *
125
+ *	@since 1.0
126
+*/
127
+function template_shd_topictoticket()
128
+{
129
+	global $txt, $settings, $context, $scripturl;
130
+	echo '
131
+		<div class="cat_bar grid_header">
132
+			<h3 class="catbg">
133
+				<img src="', $settings['default_images_url'] , '/simpledesk/topictoticket.png" alt="*" />
134
+				', $txt['shd_move_topic_to_ticket'], '
135
+			</h3>
136
+		</div>
137
+		<div class="roundframe">
138
+		<form action="', $scripturl, '?action=helpdesk;sa=topictoticket2;topic=', $context['topic_id'], '" method="post" onsubmit="submitonce(this);">
139
+			<div class="content">
140
+				<dl class="settings">
141
+					<dt>
142
+						<strong>', $txt['shd_move_send_pm_topic'], ':</strong>
143
+					</dt>
144
+					<dd>
145
+						<input type="checkbox" name="send_pm" id="send_pm" checked="checked" onclick="document.getElementById(\'pm_message\').style.display = this.checked ? \'block\' : \'none\';" class="input_check" />
146
+					</dd>
147
+				</dl>
148
+				<fieldset id="pm_message">
149
+					<dl class="settings">
150
+						<dt>
151
+							', $txt['shd_move_why_topic'], '
152
+						</dt>
153
+						<dd>
154
+							<textarea name="pm_content" rows="9" cols="70">', $txt['shd_move_default_topic'], '</textarea>
155
+						</dd>
156
+					</dl>
157
+				</fieldset>
158
+				<input type="submit" value="', $txt['shd_move_topic'], '" onclick="return submitThisOnce(this);" accesskey="s" class="button_submit" />
159
+				<input type="submit" name="cancel" value="', $txt['shd_cancel_topic'], '" accesskey="c" class="button_submit" />
160
+				<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
161
+				<input type="hidden" name="seqnum" value="', $context['form_sequence_number'], '" />
162
+			</div>
163
+		</form>
164
+		</div>
165
+		<span class="lowerframe"><span></span></span>';
166
+}
167
+
168
+?>
0 169
\ No newline at end of file
... ...
@@ -0,0 +1,478 @@
1
+<?php
2
+// Version: 1.0 Felidae; SimpleDesk front page template
3
+
4
+/**
5
+ *	This file handles displaying the blocks of tickets for the front page, as well as the slightly
6
+ *	customised views for the recycle bin and the list of resolved tickets.
7
+ *
8
+ *	@package template
9
+ *	@since 1.0
10
+*/
11
+
12
+/**
13
+ *	Display the main helpdesk view of active tickets.
14
+ *
15
+ *	This function steps through the blocks defined in SimpleDesk.php to display all the blocks that potentially would be visible, noting whether blocks have been collapsed or not, and calling to the sub-subtemplates to output collapsed and noncollapsed blocks.
16
+ *
17
+ *	All the blocks here are defined in {@link shd_main_helpdesk()} (or {@link shd_view_block()} if viewing a single block) and data gathered in {@link shd_helpdesk_listing()}.
18
+ *
19
+ *	@see template_collapsed_ticket_block()
20
+ *	@see template_ticket_block()
21
+ *	@since 1.0
22
+*/
23
+function template_main()
24
+{
25
+	global $context, $txt, $settings, $scripturl;
26
+
27
+	echo '
28
+		<div class="modbuttons clearfix margintop">';
29
+
30
+	template_button_strip($context['navigation'], 'bottom');
31
+
32
+	echo '
33
+		</div>
34
+		<div id="admin_content">
35
+		<table width="100%" class="shd_main_hd">
36
+			<tr>
37
+				<td valign="top">
38
+					<h3 class="titlebg"><span class="left"></span>
39
+						', $txt['shd_helpdesk'], '
40
+					</h3>
41
+					<span class="upperframe"><span></span></span>
42
+					<div class="roundframe">
43
+						<div class="shd_gototicket smalltext">
44
+							<form action="', $scripturl, '?action=helpdesk;sa=ticket" method="get">
45
+								', $txt['shd_go_to_ticket'], ':
46
+								<input type="hidden" name="action" value="helpdesk" />
47
+								<input type="hidden" name="sa" value="ticket" />
48
+								<input type="text" class="input_text" id="ticketJump" name="ticket" size="4" />
49
+								<input type="submit" class="button_submit" value="', $txt['shd_go'], '" onclick="shd_quickTicketJump(this.parentNode.ticketJump.value);" />
50
+							</form>
51
+						</div>
52
+						<div id="welcome">
53
+							<strong>', sprintf($txt['shd_welcome'], $context['user']['name']), '</strong><br />
54
+							', $txt['shd_' . $context['shd_home_view'] . '_greeting'], '
55
+						</div>
56
+					</div>
57
+					<span class="lowerframe"><span></span></span>
58
+					<br />';
59
+
60
+	// Start the ticket listing
61
+	$blocks = array_keys($context['ticket_blocks']);
62
+	foreach ($blocks as $block)
63
+	{
64
+		$context['current_block'] = $block;
65
+		if (!empty($context['ticket_blocks'][$block]['count']) && $context['ticket_blocks'][$block]['count'] > 10)
66
+			$context['block_link'] = $_REQUEST['sa'] == 'viewblock' ? $scripturl . '?action=helpdesk;sa=main' : $scripturl . '?action=helpdesk;sa=viewblock;block=' . $block . '#shd_block_' . $block;
67
+		else
68
+			$context['block_link'] = '';
69
+
70
+		if ($context['ticket_blocks'][$block]['collapsed'])
71
+			template_collapsed_ticket_block();
72
+		else
73
+			template_ticket_block();
74
+	}
75
+
76
+	echo '
77
+				</td>
78
+			</tr>
79
+			</table>
80
+		</div>';
81
+
82
+	//echo 'I\'m alive!!!!!!!!!!1111oneone'; 	- I had to save this :P
83
+}
84
+
85
+/**
86
+ *	Display the helpdesk view of resolved tickets.
87
+ *
88
+ *	This function steps through the blocks defined in SimpleDesk.php to display all the block of closed items.
89
+ *
90
+ *	All the blocks here are defined in {@link shd_closed_tickets()} and data gathered in {@link shd_helpdesk_listing()}.
91
+ *
92
+ *	@see template_ticket_block()
93
+ *	@since 1.0
94
+*/
95
+function template_closedtickets()
96
+{
97
+	global $context, $txt, $settings, $scripturl;
98
+
99
+	echo '
100
+		<div class="modbuttons clearfix margintop">';
101
+
102
+	template_button_strip($context['navigation'], 'bottom');
103
+
104
+	echo '
105
+		</div>
106
+		<div id="admin_content">
107
+		<table width="100%" class="shd_main_hd">
108
+			<tr>';
109
+
110
+	echo '
111
+				<td valign="top">
112
+					<h3 class="titlebg"><span class="left"></span>
113
+						', $txt['shd_helpdesk'], '
114
+					</h3>
115
+					<span class="upperframe"><span></span></span>
116
+					<div class="roundframe">
117
+						<div class="shd_gototicket smalltext">
118
+							<form action="', $scripturl, '?action=helpdesk" method="post">
119
+								', $txt['shd_go_to_ticket'], ':
120
+								<input type="text" name="ticket" size="4" />
121
+								<input type="submit" value="', $txt['shd_go'], '" />
122
+								<input type="hidden" name="sa" value="ticket" />
123
+							</form>
124
+						</div>
125
+						<div id="welcome">
126
+							<strong>', sprintf($txt['shd_welcome'], $context['user']['name']), '</strong><br />
127
+							', $txt['shd_closed_' . $context['shd_home_view'] . '_greeting'], '
128
+						</div>
129
+					</div>
130
+					<span class="lowerframe"><span></span></span>
131
+					<br />';
132
+
133
+	// Start the ticket listing
134
+	$blocks = array_keys($context['ticket_blocks']);
135
+	foreach ($blocks as $block)
136
+	{
137
+		$context['current_block'] = $block;
138
+		template_ticket_block();
139
+	}
140
+
141
+	echo '
142
+				</td>
143
+			</tr>
144
+			</table>
145
+		</div>';
146
+}
147
+
148
+/**
149
+ *	Display the helpdesk view of recycled and partly recycled tickets.
150
+ *
151
+ *	This function steps through the blocks defined in SimpleDesk.php to display all the blocks that would be related; the list of deleted tickets, and the list of tickets with deleted replies in.
152
+ *
153
+ *	All the blocks here are defined in {@link shd_recycle_bin()} and data gathered in {@link shd_helpdesk_listing()}.
154
+ *
155
+ *	@see template_ticket_block()
156
+ *	@since 1.0
157
+*/
158
+function template_recyclebin()
159
+{
160
+	global $context, $txt, $settings, $scripturl;
161
+
162
+	echo '
163
+		<div class="modbuttons clearfix margintop">';
164
+
165
+	template_button_strip($context['navigation'], 'bottom');
166
+
167
+	echo '
168
+		</div>
169
+		<div id="admin_content">
170
+		<table width="100%" class="shd_main_hd">
171
+			<tr>';
172
+
173
+	echo '
174
+				<td valign="top">
175
+					<h3 class="titlebg"><span class="left"></span>
176
+						', $txt['shd_helpdesk'], '
177
+					</h3>
178
+					<span class="upperframe"><span></span></span>
179
+					<div class="roundframe">
180
+						<div class="shd_gototicket smalltext">
181
+							<form action="', $scripturl, '?action=helpdesk" method="post">
182
+								', $txt['shd_go_to_ticket'], ':
183
+								<input type="text" name="ticket" size="4" />
184
+								<input type="submit" value="', $txt['shd_go'], '" />
185
+								<input type="hidden" name="sa" value="ticket" />
186
+							</form>
187
+						</div>
188
+						<div id="welcome">
189
+							<strong>', sprintf($txt['shd_welcome'], $context['user']['name']), '</strong><br />
190
+							', $txt['shd_recycle_greeting'], '
191
+						</div>
192
+					</div>
193
+					<span class="lowerframe"><span></span></span>
194
+					<br />';
195
+
196
+	// Loop through the crap... Uh, I mean the tickets! :)
197
+	$blocks = array_keys($context['ticket_blocks']);
198
+	foreach ($blocks as $block)
199
+	{
200
+		$context['current_block'] = $block;
201
+		template_ticket_block();
202
+	}
203
+
204
+	echo '
205
+				</td>
206
+			</tr>
207
+			</table>
208
+		</div>';
209
+}
210
+
211
+/**
212
+ *	Display a collapsed block.
213
+ *
214
+ *	In the front page, you are able to force a given block to be displayed in its entirety. When that happens, the other blocks are collapsed, so that they are present but their principle content is not.
215
+ *
216
+ *	@see template_ticket_block()
217
+ *	@since 1.0
218
+*/
219
+function template_collapsed_ticket_block()
220
+{
221
+	global $context, $txt, $settings, $scripturl;
222
+
223
+	echo '
224
+					<div class="tborder">
225
+					<h3 class="catbg"><span class="left"></span>
226
+						<img src="', $settings['default_images_url'], '/simpledesk/', $context['ticket_blocks'][$context['current_block']]['block_icon'], '" alt="*" />
227
+						', (empty($context['block_link']) ? '' : '<a href="' . $context['block_link'] . '">'), $context['ticket_blocks'][$context['current_block']]['title'], '
228
+						<span class="smalltext">(', $context['ticket_blocks'][$context['current_block']]['count'], ' ', ($context['ticket_blocks'][$context['current_block']]['count'] == 1 ? $txt['shd_count_ticket_1'] : $txt['shd_count_tickets']), ')</span>', (empty($context['block_link']) ? '' : '</a>'), '
229
+					</h3>
230
+					</div>
231
+					<br />
232
+					<br />';
233
+}
234
+
235
+/**
236
+ *	Display an individual, non-collapsed block.
237
+ *
238
+ *	Each front-page template uses this function to display a given block of tickets. It handles displaying the menu header, including ticket count, followed by all the different column types as listed in {@link shd_main_helpdesk()}, then to iterate through the ticket details to display each row (provided by {@link shd_helpdesk_listing()}.
239
+ *
240
+ *	@see shd_main_helpdesk()
241
+ *	@see shd_helpdesk_listing()
242
+ *	@since 1.0
243
+*/
244
+function template_ticket_block()
245
+{
246
+	global $context, $txt, $settings;
247
+	// $context['ticket_blocks'] = array of arrays of ticket data
248
+	// $context['current_block'] = the block to display now
249
+
250
+	echo '
251
+					<div class="tborder">
252
+					<div class="cat_bar grid_header">
253
+						<h3 id="shd_block_', $context['current_block'], '" class="catbg">
254
+							', !empty($context['ticket_blocks'][$context['current_block']]['page_index']) ? '<span class="floatright smalltext">'. $txt['pages'] . ': ' . $context['ticket_blocks'][$context['current_block']]['page_index'] . '</span>' : '', '
255
+							<img src="', $settings['default_images_url'], '/simpledesk/', $context['ticket_blocks'][$context['current_block']]['block_icon'], '" alt="*" />
256
+							', (empty($context['block_link']) ? '' : '<a href="' . $context['block_link'] . '">'), $context['ticket_blocks'][$context['current_block']]['title'], '
257
+							<span class="smalltext">(', $context['ticket_blocks'][$context['current_block']]['count'], ' ', ($context['ticket_blocks'][$context['current_block']]['count'] == 1 ? $txt['shd_count_ticket_1'] : $txt['shd_count_tickets']), ')</span>', (empty($context['block_link']) ? '' : '</a>'), '
258
+						</h3>
259
+					</div>
260
+
261
+					<table class="shd_ticketlist" cellspacing="0" width="100%">
262
+						<tr class="titlebg">';
263
+
264
+	$block_width = 0;
265
+	foreach ($context['ticket_blocks'][$context['current_block']]['columns'] as $column)
266
+	{
267
+		$block_width++;
268
+		switch ($column)
269
+		{
270
+			case 'ticket_id':
271
+				echo '
272
+							<td width="8%" colspan="2" class="shd_nowrap"><img src="', $settings['default_images_url'], '/simpledesk/ticket.png" class="shd_smallicon" alt="" /> ', template_shd_menu_header('ticketid', $txt['shd_ticket']), '</td>';
273
+				$block_width++; // is 2 blocks wide
274
+				break;
275
+			case 'ticket_name':
276
+				echo '
277
+							<td width="15%" class="shd_nowrap">', template_shd_menu_header('ticketname', $txt['shd_ticket_name']), '</td>';
278
+				break;
279
+			case 'starting_user':
280
+				echo '
281
+							<td width="12%" class="shd_nowrap"><img src="', $settings['default_images_url'], '/simpledesk/user.png" class="shd_smallicon" alt="" /> ', template_shd_menu_header('starter', $txt['shd_ticket_started_by']), '</td>';
282
+				break;
283
+			case 'last_reply':
284
+				echo '
285
+							<td width="20%" class="shd_nowrap"><img src="', $settings['default_images_url'], '/simpledesk/staff.png" class="shd_smallicon" alt="" /> ', template_shd_menu_header('lastreply', $txt['shd_ticket_updated_by']), '</td>';
286
+				break;
287
+			case 'assigned':
288
+				echo '
289
+							<td width="12%" class="shd_nowrap"><img src="', $settings['default_images_url'], '/simpledesk/staff.png" class="shd_smallicon" alt="" /> ', template_shd_menu_header('assigned', $txt['shd_ticket_assigned']), '</td>';
290
+				break;
291
+			case 'status':
292
+				echo '
293
+							<td width="17%" class="shd_nowrap"><img src="', $settings['default_images_url'], '/simpledesk/status.png" class="shd_smallicon" alt="" /> ', template_shd_menu_header('starter', $txt['shd_ticket_status']), '</td>';
294
+				break;
295
+			case 'urgency':
296
+				echo '
297
+							<td width="8%" class="shd_nowrap"><img src="', $settings['default_images_url'], '/simpledesk/urgency.png" class="shd_smallicon" alt="" /> ', template_shd_menu_header('urgency', $txt['shd_ticket_urgency']), '</td>';
298
+				break;
299
+			case 'updated':
300
+				echo '
301
+							<td width="22%" class="shd_nowrap"><img src="', $settings['default_images_url'], '/simpledesk/time.png" class="shd_smallicon" alt="" /> ', template_shd_menu_header('updated', $txt['shd_ticket_updated']), '</td>';
302
+				break;
303
+			case 'replies':
304
+				echo '
305
+							<td width="7%" class="shd_nowrap">', template_shd_menu_header('replies', $txt['shd_ticket_num_replies']), '</td>';
306
+				break;
307
+			case 'allreplies':
308
+				echo '
309
+							<td width="7%" class="shd_nowrap">', template_shd_menu_header('allreplies', $txt['shd_ticket_num_replies']), '</td>';
310
+				break;
311
+			case 'actions':
312
+				echo '
313
+							<td width="5%" class="shd_nowrap">', $txt['shd_actions'] , '</td>';
314
+				break;
315
+			default:
316
+				echo '
317
+							<td><td>';
318
+				break;
319
+		}
320
+	}
321
+
322
+	echo '
323
+						</tr>';
324
+
325
+	if (empty($context['ticket_blocks'][$context['current_block']]['tickets']))
326
+	{
327
+		echo '
328
+						<tr class="windowbg2">
329
+							<td colspan="', $block_width, '" class="shd_noticket">', $txt['shd_error_no_tickets'], '</td>
330
+						</tr>';
331
+	}
332
+	else
333
+	{
334
+		$use_bg2 = true; // start with windowbg2 to differentiate between that and titlebg
335
+		foreach ($context['ticket_blocks'][$context['current_block']]['tickets'] as $ticket)
336
+		{
337
+			echo '
338
+						<tr class="', ($use_bg2 ? 'windowbg2' : 'windowbg'), '">';
339
+
340
+			foreach ($context['ticket_blocks'][$context['current_block']]['columns'] as $column)
341
+			{
342
+				switch ($column)
343
+				{
344
+					case 'ticket_id':
345
+						$private_image = $ticket['private'] ? 'private.png' : 'public.png';
346
+						$private_txt = $ticket['private'] ? $txt['shd_ticket_private'] : $txt['shd_ticket_notprivate'];
347
+						$unread_image = $ticket['is_unread'] ? 'unread.png' : 'read.png';
348
+						$unread_txt = $ticket['is_unread'] ? $txt['shd_ticket_new'] : $txt['shd_ticket_notnew'];
349
+						echo '
350
+							<td width="1%" class="shd_nowrap">
351
+								<img src="', $settings['default_images_url'], '/simpledesk/', $unread_image, '" alt="', $unread_txt, '" title="', $unread_txt, '" />
352
+								<img src="', $settings['default_images_url'], '/simpledesk/', $private_image, '" alt="', $private_txt, '" title="', $private_txt, '" />
353
+							</td>
354
+							<td width="4%" class="smalltext">', $ticket['display_id'], '</td>';
355
+						break;
356
+					case 'ticket_name':
357
+						echo '
358
+							<td class="smalltext">', $ticket['link'], '</td>';
359
+						break;
360
+					case 'starting_user':
361
+						echo '
362
+							<td class="smalltext">', $ticket['starter']['link'], '</td>';
363
+						break;
364
+					case 'last_reply':
365
+						echo '
366
+							<td class="smalltext">', $ticket['respondent']['link'], '</td>';
367
+						break;
368
+					case 'assigned':
369
+						echo '
370
+							<td class="smalltext">' . $ticket['assigned']['link'] . '</td>';
371
+						break;
372
+					case 'status':
373
+						echo '
374
+							<td class="smalltext">', $ticket['status']['label'], '</td>';
375
+						break;
376
+					case 'urgency':
377
+						echo '
378
+							<td class="smalltext">' . $ticket['urgency']['label'] . '</td>';
379
+						break;
380
+					case 'updated':
381
+						echo '
382
+							<td class="smalltext">', $ticket['last_update'], '</td>';
383
+						break;
384
+					case 'replies':
385
+						echo '
386
+							<td class="smalltext">', $ticket['num_replies'], '</td>';
387
+						break;
388
+					case 'allreplies':
389
+						echo '
390
+							<td class="smalltext">', $ticket['all_replies'], '</td>';
391
+						break;
392
+					case 'actions':
393
+						echo '
394
+							<td class="shd_nowrap">';
395
+
396
+						foreach ($ticket['actions'] as $action)
397
+							echo '
398
+								', $action;
399
+
400
+						echo '
401
+							</td>';
402
+
403
+						break;
404
+					default:
405
+						echo '
406
+							<td><td>';
407
+						break;
408
+				}
409
+			}
410
+
411
+			echo '
412
+						</tr>';
413
+
414
+			$use_bg2 = !$use_bg2;
415
+		}
416
+	}
417
+
418
+	if (!empty($context['ticket_blocks'][$context['current_block']]['page_index']))
419
+		echo '
420
+					<tr class="titlebg">
421
+						<td colspan="', $block_width, '"><span class="floatright smalltext">', $txt['pages'], ': ', $context['ticket_blocks'][$context['current_block']]['page_index'], '</span></td>
422
+					</tr>';
423
+
424
+	echo '
425
+					</table>
426
+					</div>
427
+					<br /><br />';
428
+}
429
+
430
+/**
431
+ *	Makes a menu header clickable/sortable.
432
+ *
433
+ *	Within the ticket blocks, it is possible to sort the blocks by column, and do so in a way that is retained as you manipulate individual blocks. Since this is transient (not pushed to the database) it needs to be recorded in the URL over time.
434
+ *
435
+ *	@param string $header The identifier of the header to use here; related to {@link shd_helpdesk_listing()}
436
+ *	@param string $string The text string to use as the header text
437
+ *
438
+ *	@return string The fully formed HTML fragment for the link, text and hint image
439
+ *	@see template_ticket_block()
440
+ *	@since 1.0
441
+*/
442
+function template_shd_menu_header($header, $string)
443
+{
444
+	global $context, $scripturl, $settings;
445
+
446
+	if (empty($context['ticket_blocks'][$context['current_block']]['tickets']))
447
+		return $string; // no sense doing any work if it's an empty block and thus not sortable!
448
+
449
+	$link = '';
450
+	// Get the pages of existing items first
451
+	foreach ($context['ticket_blocks'] as $block_key => $block)
452
+	{
453
+		if (isset($_REQUEST['st_' . $block_key]))
454
+			$link .= ';st_' . $block_key . '=' . $block['start'];
455
+	}
456
+
457
+	$direction = 'down';
458
+	// Now for sorting direction per block
459
+	foreach ($context['ticket_blocks'] as $block_key => $block)
460
+	{
461
+		if (!$block['sort']['add_link'] && $block_key != $context['current_block'])
462
+			continue;
463
+
464
+		$link_direction = ($block_key == $context['current_block']) ? ($block['sort']['direction'] == 'asc' ? 'desc' : 'asc') : $block['sort']['direction'];
465
+
466
+		$link .= ';so_' . $block_key . '=' . ($block_key != $context['current_block'] ? $block['sort']['item'] : $header) . '_' . $link_direction;
467
+	}
468
+
469
+	$html = '<a href="' . $scripturl . '?action=helpdesk;sa=' . $_REQUEST['sa'] . ($_REQUEST['sa'] == 'viewblock' ? ';block=' . $_REQUEST['block'] : '') . $link . '">' . $string . '</a> ';
470
+
471
+	if ($context['ticket_blocks'][$context['current_block']]['sort']['item'] == $header)
472
+	{
473
+		$html .= '<img src="' . $settings['images_url']  . '/sort_' . ($context['ticket_blocks'][$context['current_block']]['sort']['direction'] == 'asc' ? 'down' : 'up') . '.gif" alt="" />';
474
+	}
475
+
476
+	return $html;
477
+}
478
+?>
0 479
\ No newline at end of file
... ...
@@ -0,0 +1,89 @@
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: 1.0 Felidae                             #
17
+# File Info: uninstall-optional.php / 1.0 Felidae             #
18
+###############################################################
19
+
20
+/**
21
+ *	This script removes all the extraneous data if the user requests it be removed on uninstall.
22
+ *
23
+ *	NOTE: This script is meant to run using the <samp><database></database></samp> elements of the package-info.xml file. The install
24
+ *	script, run through <samp><database></samp> elements would have set up the tables and so on. This script runs from
25
+ *	<samp><database></samp> during uninstallation only when the user requests that data should be cleared, so this script deals with it;
26
+ *	note that table removal will be dealt with by SMF itself because of <samp><database></samp> handling, so this is for things like
27
+ *	settings that are not covered in <samp><database></samp>.
28
+ *
29
+ *	@package installer
30
+ *	@since 1.0
31
+ */
32
+
33
+/**
34
+ *	Before attempting to execute, this file attempts to load SSI.php to enable access to the database functions.
35
+*/
36
+
37
+// If we have found SSI.php and we are outside of SMF, then we are running standalone.
38
+if (file_exists(dirname(__FILE__) . '/SSI.php') && !defined('SMF'))
39
+{
40
+	require_once(dirname(__FILE__) . '/SSI.php');
41
+	db_extend('packages');
42
+}
43
+// If we are outside SMF and can't find SSI.php, then throw an error
44
+elseif (!defined('SMF'))
45
+{
46
+	die('<b>Error:</b> Cannot uninstall - please verify you put this file in the same place as SMF\'s SSI.php.');
47
+}
48
+
49
+$to_remove = array(
50
+	'shd_staff_badge',
51
+	'shd_display_avatar',
52
+	'shd_disable_action_log',
53
+	'shd_ticketnav_style',
54
+	'shd_allow_ticket_bbc',
55
+	'shd_allow_ticket_smileys',
56
+	'shd_enabled_bbc',
57
+	'shd_helpdesk_only',
58
+	'shd_attachments_mode',
59
+	'shd_disable_pm',
60
+	'shd_disable_mlist',
61
+	'shd_attachments_mode',
62
+	'shd_bbc',
63
+	'shd_staff_ticket_self',
64
+	'shd_admins_not_assignable',
65
+	'shd_privacy_display',
66
+);
67
+
68
+global $modSettings;
69
+
70
+// Clear in-situ modsettings
71
+foreach ($to_remove as $setting)
72
+	if (isset($modSettings[$setting]))
73
+		unset($modSettings[$setting]);
74
+
75
+// Remove from the database; updateSettings can't actually remove them, it seems :(
76
+$smcFunc['db_query']('', '
77
+	DELETE FROM {db_prefix}settings
78
+	WHERE variable IN ({array_string:settings})',
79
+	array(
80
+		'settings' => $to_remove,
81
+	)
82
+);
83
+
84
+// And tell SMF we've updated $modSettings
85
+updateSettings(array(
86
+	'settings_updated' => time(),
87
+));
88
+
89
+?>
0 90
\ No newline at end of file
... ...
@@ -0,0 +1,61 @@
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: 1.0 Felidae                             #
17
+# File Info: uninstall-required.php / 1.0 Felidae             #
18
+###############################################################
19
+
20
+/**
21
+ *	This script removes all the extraneous data if the user requests it be removed on uninstall.
22
+ *
23
+ *	NOTE: This script is meant to run using the <samp><code></code></samp> elements of our package-info.xml file. This is because
24
+ *	certain items in the database and within SMF will need to be removed regardless of whether the user wants to keep data or not,
25
+ *	for example SimpleDesk needs to be deactivated in the Core Features panel, which must be done regardless of whether the user
26
+ *	wanted to keep data or not during uninstallation. Future expansions may add items here, especially such as scheduled tasks.
27
+ *
28
+ *	@package installer
29
+ *	@since 1.0
30
+ */
31
+
32
+/**
33
+ *	Before attempting to execute, this file attempts to load SSI.php to enable access to the database functions.
34
+*/
35
+
36
+// If we have found SSI.php and we are outside of SMF, then we are running standalone.
37
+if (file_exists(dirname(__FILE__) . '/SSI.php') && !defined('SMF'))
38
+{
39
+	require_once(dirname(__FILE__) . '/SSI.php');
40
+	db_extend('packages');
41
+}
42
+// If we are outside SMF and can't find SSI.php, then throw an error
43
+elseif (!defined('SMF'))
44
+{
45
+	die('<b>Error:</b> Cannot uninstall - please verify you put this file in the same place as SMF\'s SSI.php.');
46
+}
47
+
48
+// 1. Unchecking the option in Core Features.
49
+
50
+global $modSettings;
51
+$modSettings['helpdesk_active'] = false;
52
+$features = implode(',', array_diff(explode(',', $modSettings['admin_features']), array('shd')));
53
+
54
+updateSettings(
55
+	array(
56
+		'admin_features' => $features,
57
+	),
58
+	true
59
+);
60
+
61
+?>
0 62
\ No newline at end of file
1 63