Issues (1027)

Sources/Drafts.php (1 issue)

1
<?php
2
3
/**
4
 * This file contains all the functions that allow for the saving,
5
 * retrieving, deleting and settings for the drafts function.
6
 *
7
 * Simple Machines Forum (SMF)
8
 *
9
 * @package SMF
10
 * @author Simple Machines http://www.simplemachines.org
11
 * @copyright 2019 Simple Machines and individual contributors
12
 * @license http://www.simplemachines.org/about/smf/license.php BSD
13
 *
14
 * @version 2.1 RC2
15
 */
16
17
if (!defined('SMF'))
18
	die('No direct access...');
19
20
loadLanguage('Drafts');
21
22
/**
23
 * Saves a post draft in the user_drafts table
24
 * The core draft feature must be enabled, as well as the post draft option
25
 * Determines if this is a new or an existing draft
26
 * Returns errors in $post_errors for display in the template
27
 *
28
 * @param string[] $post_errors Any errors encountered trying to save this draft
29
 * @return boolean Always returns true
30
 */
31
function SaveDraft(&$post_errors)
32
{
33
	global $context, $user_info, $smcFunc, $modSettings, $board;
34
35
	// can you be, should you be ... here?
36
	if (empty($modSettings['drafts_post_enabled']) || !allowedTo('post_draft') || !isset($_POST['save_draft']) || !isset($_POST['id_draft']))
37
		return false;
38
39
	// read in what they sent us, if anything
40
	$id_draft = (int) $_POST['id_draft'];
41
	$draft_info = ReadDraft($id_draft);
42
43
	// A draft has been saved less than 5 seconds ago, let's not do the autosave again
44
	if (isset($_REQUEST['xml']) && !empty($draft_info['poster_time']) && time() < $draft_info['poster_time'] + 5)
45
	{
46
		$context['draft_saved_on'] = $draft_info['poster_time'];
47
48
		// since we were called from the autosave function, send something back
49
		if (!empty($id_draft))
50
			XmlDraft($id_draft);
51
52
		return true;
53
	}
54
55
	if (!isset($_POST['message']))
56
		$_POST['message'] = isset($_POST['quickReply']) ? $_POST['quickReply'] : '';
57
58
	// prepare any data from the form
59
	$topic_id = empty($_REQUEST['topic']) ? 0 : (int) $_REQUEST['topic'];
60
	$draft['icon'] = empty($_POST['icon']) ? 'xx' : preg_replace('~[\./\\\\*:"\'<>]~', '', $_POST['icon']);
61
	$draft['smileys_enabled'] = isset($_POST['ns']) ? (int) $_POST['ns'] : 1;
62
	$draft['locked'] = isset($_POST['lock']) ? (int) $_POST['lock'] : 0;
63
	$draft['sticky'] = isset($_POST['sticky']) ? (int) $_POST['sticky'] : 0;
64
	$draft['subject'] = strtr($smcFunc['htmlspecialchars']($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
65
	$draft['body'] = $smcFunc['htmlspecialchars']($_POST['message'], ENT_QUOTES);
66
67
	// message and subject still need a bit more work
68
	preparsecode($draft['body']);
69
	if ($smcFunc['strlen']($draft['subject']) > 100)
70
		$draft['subject'] = $smcFunc['substr']($draft['subject'], 0, 100);
71
72
	// Modifying an existing draft, like hitting the save draft button or autosave enabled?
73
	if (!empty($id_draft) && !empty($draft_info))
74
	{
75
		$smcFunc['db_query']('', '
76
			UPDATE {db_prefix}user_drafts
77
			SET
78
				id_topic = {int:id_topic},
79
				id_board = {int:id_board},
80
				poster_time = {int:poster_time},
81
				subject = {string:subject},
82
				smileys_enabled = {int:smileys_enabled},
83
				body = {string:body},
84
				icon = {string:icon},
85
				locked = {int:locked},
86
				is_sticky = {int:is_sticky}
87
			WHERE id_draft = {int:id_draft}',
88
			array(
89
				'id_topic' => $topic_id,
90
				'id_board' => $board,
91
				'poster_time' => time(),
92
				'subject' => $draft['subject'],
93
				'smileys_enabled' => (int) $draft['smileys_enabled'],
94
				'body' => $draft['body'],
95
				'icon' => $draft['icon'],
96
				'locked' => $draft['locked'],
97
				'is_sticky' => $draft['sticky'],
98
				'id_draft' => $id_draft,
99
			)
100
		);
101
102
		// some items to return to the form
103
		$context['draft_saved'] = true;
104
		$context['id_draft'] = $id_draft;
105
106
		// cleanup
107
		unset($_POST['save_draft']);
108
	}
109
	// otherwise creating a new draft
110
	else
111
	{
112
		$id_draft = $smcFunc['db_insert']('',
113
			'{db_prefix}user_drafts',
114
			array(
115
				'id_topic' => 'int',
116
				'id_board' => 'int',
117
				'type' => 'int',
118
				'poster_time' => 'int',
119
				'id_member' => 'int',
120
				'subject' => 'string-255',
121
				'smileys_enabled' => 'int',
122
				'body' => (!empty($modSettings['max_messageLength']) && $modSettings['max_messageLength'] > 65534 ? 'string-' . $modSettings['max_messageLength'] : 'string-65534'),
123
				'icon' => 'string-16',
124
				'locked' => 'int',
125
				'is_sticky' => 'int'
126
			),
127
			array(
128
				$topic_id,
129
				$board,
130
				0,
131
				time(),
132
				$user_info['id'],
133
				$draft['subject'],
134
				$draft['smileys_enabled'],
135
				$draft['body'],
136
				$draft['icon'],
137
				$draft['locked'],
138
				$draft['sticky']
139
			),
140
			array(
141
				'id_draft'
142
			),
143
			1
144
		);
145
146
		// everything go as expected?
147
		if (!empty($id_draft))
148
		{
149
			$context['draft_saved'] = true;
150
			$context['id_draft'] = $id_draft;
151
		}
152
		else
153
			$post_errors[] = 'draft_not_saved';
154
155
		// cleanup
156
		unset($_POST['save_draft']);
157
	}
158
159
	// if we were called from the autosave function, send something back
160
	if (!empty($id_draft) && isset($_REQUEST['xml']) && (!in_array('session_timeout', $post_errors)))
161
	{
162
		$context['draft_saved_on'] = time();
163
		XmlDraft($id_draft);
164
	}
165
166
	return true;
167
}
168
169
/**
170
 * Saves a PM draft in the user_drafts table
171
 * The core draft feature must be enabled, as well as the pm draft option
172
 * Determines if this is a new or and update to an existing pm draft
173
 *
174
 * @param string $post_errors A string of info about errors encountered trying to save this draft
175
 * @param array $recipientList An array of data about who this PM is being sent to
176
 * @return boolean false if you can't save the draft, true if we're doing this via XML more than 5 seconds after the last save, nothing otherwise
177
 */
178
function SavePMDraft(&$post_errors, $recipientList)
179
{
180
	global $context, $user_info, $smcFunc, $modSettings;
181
182
	// PM survey says ... can you stay or must you go
183
	if (empty($modSettings['drafts_pm_enabled']) || !allowedTo('pm_draft') || !isset($_POST['save_draft']))
184
		return false;
185
186
	// read in what you sent us
187
	$id_pm_draft = (int) $_POST['id_pm_draft'];
188
	$draft_info = ReadDraft($id_pm_draft, 1);
189
190
	// 5 seconds is the same limit we have for posting
191
	if (isset($_REQUEST['xml']) && !empty($draft_info['poster_time']) && time() < $draft_info['poster_time'] + 5)
192
	{
193
		$context['draft_saved_on'] = $draft_info['poster_time'];
194
195
		// Send something back to the javascript caller
196
		if (!empty($id_draft))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $id_draft does not exist. Did you maybe mean $id_pm_draft?
Loading history...
197
			XmlDraft($id_draft);
198
199
		return true;
200
	}
201
202
	// determine who this is being sent to
203
	if (isset($_REQUEST['xml']))
204
	{
205
		$recipientList['to'] = isset($_POST['recipient_to']) ? explode(',', $_POST['recipient_to']) : array();
206
		$recipientList['bcc'] = isset($_POST['recipient_bcc']) ? explode(',', $_POST['recipient_bcc']) : array();
207
	}
208
	elseif (!empty($draft_info['to_list']) && empty($recipientList))
209
		$recipientList = $smcFunc['json_decode']($draft_info['to_list'], true);
210
211
	// prepare the data we got from the form
212
	$reply_id = empty($_POST['replied_to']) ? 0 : (int) $_POST['replied_to'];
213
	$draft['body'] = $smcFunc['htmlspecialchars']($_POST['message'], ENT_QUOTES);
214
	$draft['subject'] = strtr($smcFunc['htmlspecialchars']($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
215
216
	// message and subject always need a bit more work
217
	preparsecode($draft['body']);
218
	if ($smcFunc['strlen']($draft['subject']) > 100)
219
		$draft['subject'] = $smcFunc['substr']($draft['subject'], 0, 100);
220
221
	// Modifying an existing PM draft?
222
	if (!empty($id_pm_draft) && !empty($draft_info))
223
	{
224
		$smcFunc['db_query']('', '
225
			UPDATE {db_prefix}user_drafts
226
			SET id_reply = {int:id_reply},
227
				type = {int:type},
228
				poster_time = {int:poster_time},
229
				subject = {string:subject},
230
				body = {string:body},
231
				to_list = {string:to_list}
232
			WHERE id_draft = {int:id_pm_draft}
233
			LIMIT 1',
234
			array(
235
				'id_reply' => $reply_id,
236
				'type' => 1,
237
				'poster_time' => time(),
238
				'subject' => $draft['subject'],
239
				'body' => $draft['body'],
240
				'id_pm_draft' => $id_pm_draft,
241
				'to_list' => $smcFunc['json_encode']($recipientList),
242
			)
243
		);
244
245
		// some items to return to the form
246
		$context['draft_saved'] = true;
247
		$context['id_pm_draft'] = $id_pm_draft;
248
	}
249
	// otherwise creating a new PM draft.
250
	else
251
	{
252
		$id_pm_draft = $smcFunc['db_insert']('',
253
			'{db_prefix}user_drafts',
254
			array(
255
				'id_reply' => 'int',
256
				'type' => 'int',
257
				'poster_time' => 'int',
258
				'id_member' => 'int',
259
				'subject' => 'string-255',
260
				'body' => 'string-65534',
261
				'to_list' => 'string-255',
262
			),
263
			array(
264
				$reply_id,
265
				1,
266
				time(),
267
				$user_info['id'],
268
				$draft['subject'],
269
				$draft['body'],
270
				$smcFunc['json_encode']($recipientList),
271
			),
272
			array(
273
				'id_draft'
274
			),
275
			1
276
		);
277
278
		// everything go as expected, if not toss back an error
279
		if (!empty($id_pm_draft))
280
		{
281
			$context['draft_saved'] = true;
282
			$context['id_pm_draft'] = $id_pm_draft;
283
		}
284
		else
285
			$post_errors[] = 'draft_not_saved';
286
	}
287
288
	// if we were called from the autosave function, send something back
289
	if (!empty($id_pm_draft) && isset($_REQUEST['xml']) && !in_array('session_timeout', $post_errors))
290
	{
291
		$context['draft_saved_on'] = time();
292
		XmlDraft($id_pm_draft);
293
	}
294
295
	return;
296
}
297
298
/**
299
 * Reads a draft in from the user_drafts table
300
 * Validates that the draft is the user''s draft
301
 * Optionally loads the draft in to context or superglobal for loading in to the form
302
 *
303
 * @param int $id_draft ID of the draft to load
304
 * @param int $type Type of draft - 0 for post or 1 for PM
305
 * @param boolean $check Validate that this draft belongs to the current user
306
 * @param boolean $load Whether or not to load the data into variables for use on a form
307
 * @return boolean|array False if the data couldn't be loaded, true if it's a PM draft or an array of info about the draft if it's a post draft
308
 */
309
function ReadDraft($id_draft, $type = 0, $check = true, $load = false)
310
{
311
	global $context, $user_info, $smcFunc, $modSettings;
312
313
	// like purell always clean to be sure
314
	$id_draft = (int) $id_draft;
315
	$type = (int) $type;
316
317
	// nothing to read, nothing to do
318
	if (empty($id_draft))
319
		return false;
320
321
	// load in this draft from the DB
322
	$request = $smcFunc['db_query']('', '
323
		SELECT is_sticky, locked, smileys_enabled, icon, body , subject,
324
			id_board, id_draft, id_reply, to_list
325
		FROM {db_prefix}user_drafts
326
		WHERE id_draft = {int:id_draft}' . ($check ? '
327
			AND id_member = {int:id_member}' : '') . '
328
			AND type = {int:type}' . (!empty($modSettings['drafts_keep_days']) ? '
329
			AND poster_time > {int:time}' : '') . '
330
		LIMIT 1',
331
		array(
332
			'id_member' => $user_info['id'],
333
			'id_draft' => $id_draft,
334
			'type' => $type,
335
			'time' => (!empty($modSettings['drafts_keep_days']) ? (time() - ($modSettings['drafts_keep_days'] * 86400)) : 0),
336
		)
337
	);
338
339
	// no results?
340
	if (!$smcFunc['db_num_rows']($request))
341
		return false;
342
343
	// load up the data
344
	$draft_info = $smcFunc['db_fetch_assoc']($request);
345
	$smcFunc['db_free_result']($request);
346
347
	// Load it up for the templates as well
348
	if (!empty($load))
349
	{
350
		if ($type === 0)
351
		{
352
			// a standard post draft?
353
			$context['sticky'] = !empty($draft_info['is_sticky']) ? $draft_info['is_sticky'] : '';
354
			$context['locked'] = !empty($draft_info['locked']) ? $draft_info['locked'] : '';
355
			$context['use_smileys'] = !empty($draft_info['smileys_enabled']) ? true : false;
356
			$context['icon'] = !empty($draft_info['icon']) ? $draft_info['icon'] : 'xx';
357
			$context['message'] = !empty($draft_info['body']) ? str_replace('<br>', "\n", un_htmlspecialchars(stripslashes($draft_info['body']))) : '';
358
			$context['subject'] = !empty($draft_info['subject']) ? stripslashes($draft_info['subject']) : '';
359
			$context['board'] = !empty($draft_info['id_board']) ? $draft_info['id_board'] : '';
360
			$context['id_draft'] = !empty($draft_info['id_draft']) ? $draft_info['id_draft'] : 0;
361
		}
362
		elseif ($type === 1)
363
		{
364
			// one of those pm drafts? then set it up like we have an error
365
			$_REQUEST['subject'] = !empty($draft_info['subject']) ? stripslashes($draft_info['subject']) : '';
366
			$_REQUEST['message'] = !empty($draft_info['body']) ? str_replace('<br>', "\n", un_htmlspecialchars(stripslashes($draft_info['body']))) : '';
367
			$_REQUEST['replied_to'] = !empty($draft_info['id_reply']) ? $draft_info['id_reply'] : 0;
368
			$context['id_pm_draft'] = !empty($draft_info['id_draft']) ? $draft_info['id_draft'] : 0;
369
			$recipients = $smcFunc['json_decode']($draft_info['to_list'], true);
370
371
			// make sure we only have integers in this array
372
			$recipients['to'] = array_map('intval', $recipients['to']);
373
			$recipients['bcc'] = array_map('intval', $recipients['bcc']);
374
375
			// pretend we messed up to populate the pm message form
376
			messagePostError(array(), array(), $recipients);
377
			return true;
378
		}
379
	}
380
381
	return $draft_info;
382
}
383
384
/**
385
 * Deletes one or many drafts from the DB
386
 * Validates the drafts are from the user
387
 * is supplied an array of drafts will attempt to remove all of them
388
 *
389
 * @param int $id_draft The ID of the draft to delete
390
 * @param boolean $check Whether or not to check that the draft belongs to the current user
391
 * @return boolean False if it couldn't be deleted (doesn't return anything otherwise)
392
 */
393
function DeleteDraft($id_draft, $check = true)
394
{
395
	global $user_info, $smcFunc;
396
397
	// Only a single draft.
398
	if (is_numeric($id_draft))
399
		$id_draft = array($id_draft);
400
401
	// can't delete nothing
402
	if (empty($id_draft) || ($check && empty($user_info['id'])))
403
		return false;
404
405
	$smcFunc['db_query']('', '
406
		DELETE FROM {db_prefix}user_drafts
407
		WHERE id_draft IN ({array_int:id_draft})' . ($check ? '
408
			AND  id_member = {int:id_member}' : ''),
409
		array(
410
			'id_draft' => $id_draft,
411
			'id_member' => empty($user_info['id']) ? -1 : $user_info['id'],
412
		)
413
	);
414
}
415
416
/**
417
 * Loads in a group of drafts for the user of a given type (0/posts, 1/pm's)
418
 * loads a specific draft for forum use if selected.
419
 * Used in the posting screens to allow draft selection
420
 * Will load a draft if selected is supplied via post
421
 *
422
 * @param int $member_id ID of the member to show drafts for
423
 * @param boolean|integer $topic If $type is 1, this can be set to only load drafts for posts in the specific topic
424
 * @param int $draft_type The type of drafts to show - 0 for post drafts, 1 for PM drafts
425
 * @return boolean False if the drafts couldn't be loaded, nothing otherwise
426
 */
427
function ShowDrafts($member_id, $topic = false, $draft_type = 0)
428
{
429
	global $smcFunc, $scripturl, $context, $txt, $modSettings;
430
431
	// Permissions
432
	if (($draft_type === 0 && empty($context['drafts_save'])) || ($draft_type === 1 && empty($context['drafts_pm_save'])) || empty($member_id))
433
		return false;
434
435
	$context['drafts'] = array();
436
437
	// has a specific draft has been selected?  Load it up if there is not a message already in the editor
438
	if (isset($_REQUEST['id_draft']) && empty($_POST['subject']) && empty($_POST['message']))
439
		ReadDraft((int) $_REQUEST['id_draft'], $draft_type, true, true);
440
441
	// load the drafts this user has available
442
	$request = $smcFunc['db_query']('', '
443
		SELECT subject, poster_time, id_board, id_topic, id_draft
444
		FROM {db_prefix}user_drafts
445
		WHERE id_member = {int:id_member}' . ((!empty($topic) && empty($draft_type)) ? '
446
			AND id_topic = {int:id_topic}' : (!empty($topic) ? '
447
			AND id_reply = {int:id_topic}' : '')) . '
448
			AND type = {int:draft_type}' . (!empty($modSettings['drafts_keep_days']) ? '
449
			AND poster_time > {int:time}' : '') . '
450
		ORDER BY poster_time DESC',
451
		array(
452
			'id_member' => $member_id,
453
			'id_topic' => (int) $topic,
454
			'draft_type' => $draft_type,
455
			'time' => (!empty($modSettings['drafts_keep_days']) ? (time() - ($modSettings['drafts_keep_days'] * 86400)) : 0),
456
		)
457
	);
458
459
	// add them to the draft array for display
460
	while ($row = $smcFunc['db_fetch_assoc']($request))
461
	{
462
		if (empty($row['subject']))
463
			$row['subject'] = $txt['no_subject'];
464
465
		// Post drafts
466
		if ($draft_type === 0)
467
		{
468
			$tmp_subject = shorten_subject(stripslashes($row['subject']), 24);
469
			$context['drafts'][] = array(
470
				'subject' => censorText($tmp_subject),
471
				'poster_time' => timeformat($row['poster_time']),
472
				'link' => '<a href="' . $scripturl . '?action=post;board=' . $row['id_board'] . ';' . (!empty($row['id_topic']) ? 'topic=' . $row['id_topic'] . '.0;' : '') . 'id_draft=' . $row['id_draft'] . '">' . $row['subject'] . '</a>',
473
			);
474
		}
475
		// PM drafts
476
		elseif ($draft_type === 1)
477
		{
478
			$tmp_subject = shorten_subject(stripslashes($row['subject']), 24);
479
			$context['drafts'][] = array(
480
				'subject' => censorText($tmp_subject),
481
				'poster_time' => timeformat($row['poster_time']),
482
				'link' => '<a href="' . $scripturl . '?action=pm;sa=send;id_draft=' . $row['id_draft'] . '">' . (!empty($row['subject']) ? $row['subject'] : $txt['drafts_none']) . '</a>',
483
			);
484
		}
485
	}
486
	$smcFunc['db_free_result']($request);
487
}
488
489
/**
490
 * Returns an xml response to an autosave ajax request
491
 * provides the id of the draft saved and the time it was saved
492
 *
493
 * @param int $id_draft
494
 */
495
function XmlDraft($id_draft)
496
{
497
	global $txt, $context;
498
499
	header('content-type: text/xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set']));
500
501
	echo '<?xml version="1.0" encoding="', $context['character_set'], '"?>
502
	<drafts>
503
		<draft id="', $id_draft, '"><![CDATA[', $txt['draft_saved_on'], ': ', timeformat($context['draft_saved_on']), ']]></draft>
504
	</drafts>';
505
506
	obExit(false);
507
}
508
509
/**
510
 * Show all drafts of a given type by the current user
511
 * Uses the showdraft template
512
 * Allows for the deleting and loading/editing of drafts
513
 *
514
 * @param int $memID
515
 * @param int $draft_type
516
 */
517
function showProfileDrafts($memID, $draft_type = 0)
518
{
519
	global $txt, $scripturl, $modSettings, $context, $smcFunc, $options;
520
521
	// Some initial context.
522
	$context['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0;
523
	$context['current_member'] = $memID;
524
525
	// If just deleting a draft, do it and then redirect back.
526
	if (!empty($_REQUEST['delete']))
527
	{
528
		checkSession('get');
529
		$id_delete = (int) $_REQUEST['delete'];
530
531
		$smcFunc['db_query']('', '
532
			DELETE FROM {db_prefix}user_drafts
533
			WHERE id_draft = {int:id_draft}
534
				AND id_member = {int:id_member}
535
				AND type = {int:draft_type}',
536
			array(
537
				'id_draft' => $id_delete,
538
				'id_member' => $memID,
539
				'draft_type' => $draft_type,
540
			)
541
		);
542
543
		redirectexit('action=profile;u=' . $memID . ';area=showdrafts;start=' . $context['start']);
544
	}
545
546
	// Default to 10.
547
	if (empty($_REQUEST['viewscount']) || !is_numeric($_REQUEST['viewscount']))
548
		$_REQUEST['viewscount'] = 10;
549
550
	// Get the count of applicable drafts on the boards they can (still) see ...
551
	// @todo .. should we just let them see their drafts even if they have lost board access ?
552
	$request = $smcFunc['db_query']('', '
553
		SELECT COUNT(id_draft)
554
		FROM {db_prefix}user_drafts AS ud
555
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = ud.id_board AND {query_see_board})
556
		WHERE id_member = {int:id_member}
557
			AND type={int:draft_type}' . (!empty($modSettings['drafts_keep_days']) ? '
558
			AND poster_time > {int:time}' : ''),
559
		array(
560
			'id_member' => $memID,
561
			'draft_type' => $draft_type,
562
			'time' => (!empty($modSettings['drafts_keep_days']) ? (time() - ($modSettings['drafts_keep_days'] * 86400)) : 0),
563
		)
564
	);
565
	list ($msgCount) = $smcFunc['db_fetch_row']($request);
566
	$smcFunc['db_free_result']($request);
567
568
	$maxPerPage = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
569
	$maxIndex = $maxPerPage;
570
571
	// Make sure the starting place makes sense and construct our friend the page index.
572
	$context['page_index'] = constructPageIndex($scripturl . '?action=profile;u=' . $memID . ';area=showdrafts', $context['start'], $msgCount, $maxIndex);
573
	$context['current_page'] = $context['start'] / $maxIndex;
574
575
	// Reverse the query if we're past 50% of the pages for better performance.
576
	$start = $context['start'];
577
	$reverse = $_REQUEST['start'] > $msgCount / 2;
578
	if ($reverse)
579
	{
580
		$maxIndex = $msgCount < $context['start'] + $maxPerPage + 1 && $msgCount > $context['start'] ? $msgCount - $context['start'] : $maxPerPage;
581
		$start = $msgCount < $context['start'] + $maxPerPage + 1 || $msgCount < $context['start'] + $maxPerPage ? 0 : $msgCount - $context['start'] - $maxPerPage;
582
	}
583
584
	// Find this user's drafts for the boards they can access
585
	// @todo ... do we want to do this?  If they were able to create a draft, do we remove thier access to said draft if they loose
586
	//           access to the board or if the topic moves to a board they can not see?
587
	$request = $smcFunc['db_query']('', '
588
		SELECT
589
			b.id_board, b.name AS bname,
590
			ud.id_member, ud.id_draft, ud.body, ud.smileys_enabled, ud.subject, ud.poster_time, ud.icon, ud.id_topic, ud.locked, ud.is_sticky
591
		FROM {db_prefix}user_drafts AS ud
592
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = ud.id_board AND {query_see_board})
593
		WHERE ud.id_member = {int:current_member}
594
			AND type = {int:draft_type}' . (!empty($modSettings['drafts_keep_days']) ? '
595
			AND poster_time > {int:time}' : '') . '
596
		ORDER BY ud.id_draft ' . ($reverse ? 'ASC' : 'DESC') . '
597
		LIMIT {int:start}, {int:max}',
598
		array(
599
			'current_member' => $memID,
600
			'draft_type' => $draft_type,
601
			'time' => (!empty($modSettings['drafts_keep_days']) ? (time() - ($modSettings['drafts_keep_days'] * 86400)) : 0),
602
			'start' => $start,
603
			'max' => $maxIndex,
604
		)
605
	);
606
607
	// Start counting at the number of the first message displayed.
608
	$counter = $reverse ? $context['start'] + $maxIndex + 1 : $context['start'];
609
	$context['posts'] = array();
610
	while ($row = $smcFunc['db_fetch_assoc']($request))
611
	{
612
		// Censor....
613
		if (empty($row['body']))
614
			$row['body'] = '';
615
616
		$row['subject'] = $smcFunc['htmltrim']($row['subject']);
617
		if (empty($row['subject']))
618
			$row['subject'] = $txt['no_subject'];
619
620
		censorText($row['body']);
621
		censorText($row['subject']);
622
623
		// BBC-ilize the message.
624
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], 'draft' . $row['id_draft']);
625
626
		// And the array...
627
		$context['drafts'][$counter += $reverse ? -1 : 1] = array(
628
			'body' => $row['body'],
629
			'counter' => $counter,
630
			'board' => array(
631
				'name' => $row['bname'],
632
				'id' => $row['id_board']
633
			),
634
			'topic' => array(
635
				'id' => $row['id_topic'],
636
				'link' => empty($row['id']) ? $row['subject'] : '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['subject'] . '</a>',
637
			),
638
			'subject' => $row['subject'],
639
			'time' => timeformat($row['poster_time']),
640
			'timestamp' => forum_time(true, $row['poster_time']),
641
			'icon' => $row['icon'],
642
			'id_draft' => $row['id_draft'],
643
			'locked' => $row['locked'],
644
			'sticky' => $row['is_sticky'],
645
			'quickbuttons' => array(
646
				'edit' => array(
647
					'label' => $txt['draft_edit'],
648
					'href' => $scripturl.'?action=post;'.(empty($row['id_topic']) ? 'board='.$row['id_board'] : 'topic='.$row['id_topic']).'.0;id_draft='.$row['id_draft'],
649
					'icon' => 'modify_button'
650
				),
651
				'delete' => array(
652
					'label' => $txt['draft_delete'],
653
					'href' => $scripturl.'?action=profile;u='.$context['member']['id'].';area=showdrafts;delete='.$row['id_draft'].';'.$context['session_var'].'='.$context['session_id'],
654
					'javascript' => 'data-confirm="'.$txt['draft_remove'].'" class="you_sure"',
655
					'icon' => 'remove_button'
656
				),
657
			),
658
		);
659
	}
660
	$smcFunc['db_free_result']($request);
661
662
	// If the drafts were retrieved in reverse order, get them right again.
663
	if ($reverse)
664
		$context['drafts'] = array_reverse($context['drafts'], true);
665
666
	// Menu tab
667
	$context[$context['profile_menu_name']]['tab_data'] = array(
668
		'title' => $txt['drafts_show'],
669
		'description' => $txt['drafts_show_desc'],
670
		'icon_class' => 'pm_icons inbox'
671
	);
672
	$context['sub_template'] = 'showDrafts';
673
}
674
675
/**
676
 * Show all PM drafts of the current user
677
 * Uses the showpmdraft template
678
 * Allows for the deleting and loading/editing of drafts
679
 *
680
 * @param int $memID
681
 */
682
function showPMDrafts($memID = -1)
683
{
684
	global $txt, $user_info, $scripturl, $modSettings, $context, $smcFunc, $options;
685
686
	// init
687
	$draft_type = 1;
688
689
	// If just deleting a draft, do it and then redirect back.
690
	if (!empty($_REQUEST['delete']))
691
	{
692
		checkSession('get');
693
		$id_delete = (int) $_REQUEST['delete'];
694
		$start = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0;
695
696
		$smcFunc['db_query']('', '
697
			DELETE FROM {db_prefix}user_drafts
698
			WHERE id_draft = {int:id_draft}
699
				AND id_member = {int:id_member}
700
				AND type = {int:draft_type}
701
			LIMIT 1',
702
			array(
703
				'id_draft' => $id_delete,
704
				'id_member' => $memID,
705
				'draft_type' => $draft_type,
706
			)
707
		);
708
709
		// now redirect back to the list
710
		redirectexit('action=pm;sa=showpmdrafts;start=' . $start);
711
	}
712
713
	// perhaps a draft was selected for editing? if so pass this off
714
	if (!empty($_REQUEST['id_draft']) && !empty($context['drafts_pm_save']) && $memID == $user_info['id'])
715
	{
716
		checkSession('get');
717
		$id_draft = (int) $_REQUEST['id_draft'];
718
		redirectexit('action=pm;sa=send;id_draft=' . $id_draft);
719
	}
720
721
	// Default to 10.
722
	if (empty($_REQUEST['viewscount']) || !is_numeric($_REQUEST['viewscount']))
723
		$_REQUEST['viewscount'] = 10;
724
725
	// Get the count of applicable drafts
726
	$request = $smcFunc['db_query']('', '
727
		SELECT COUNT(id_draft)
728
		FROM {db_prefix}user_drafts
729
		WHERE id_member = {int:id_member}
730
			AND type={int:draft_type}' . (!empty($modSettings['drafts_keep_days']) ? '
731
			AND poster_time > {int:time}' : ''),
732
		array(
733
			'id_member' => $memID,
734
			'draft_type' => $draft_type,
735
			'time' => (!empty($modSettings['drafts_keep_days']) ? (time() - ($modSettings['drafts_keep_days'] * 86400)) : 0),
736
		)
737
	);
738
	list ($msgCount) = $smcFunc['db_fetch_row']($request);
739
	$smcFunc['db_free_result']($request);
740
741
	$maxPerPage = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
742
	$maxIndex = $maxPerPage;
743
744
	// Make sure the starting place makes sense and construct our friend the page index.
745
	$context['page_index'] = constructPageIndex($scripturl . '?action=pm;sa=showpmdrafts', $context['start'], $msgCount, $maxIndex);
746
	$context['current_page'] = $context['start'] / $maxIndex;
747
748
	// Reverse the query if we're past 50% of the total for better performance.
749
	$start = $context['start'];
750
	$reverse = $_REQUEST['start'] > $msgCount / 2;
751
	if ($reverse)
752
	{
753
		$maxIndex = $msgCount < $context['start'] + $maxPerPage + 1 && $msgCount > $context['start'] ? $msgCount - $context['start'] : $maxPerPage;
754
		$start = $msgCount < $context['start'] + $maxPerPage + 1 || $msgCount < $context['start'] + $maxPerPage ? 0 : $msgCount - $context['start'] - $maxPerPage;
755
	}
756
757
	// Load in this user's PM drafts
758
	$request = $smcFunc['db_query']('', '
759
		SELECT
760
			ud.id_member, ud.id_draft, ud.body, ud.subject, ud.poster_time, ud.id_reply, ud.to_list
761
		FROM {db_prefix}user_drafts AS ud
762
		WHERE ud.id_member = {int:current_member}
763
			AND type = {int:draft_type}' . (!empty($modSettings['drafts_keep_days']) ? '
764
			AND poster_time > {int:time}' : '') . '
765
		ORDER BY ud.id_draft ' . ($reverse ? 'ASC' : 'DESC') . '
766
		LIMIT {int:start}, {int:max}',
767
		array(
768
			'current_member' => $memID,
769
			'draft_type' => $draft_type,
770
			'time' => (!empty($modSettings['drafts_keep_days']) ? (time() - ($modSettings['drafts_keep_days'] * 86400)) : 0),
771
			'start' => $start,
772
			'max' => $maxIndex,
773
		)
774
	);
775
776
	// Start counting at the number of the first message displayed.
777
	$counter = $reverse ? $context['start'] + $maxIndex + 1 : $context['start'];
778
	$context['posts'] = array();
779
	while ($row = $smcFunc['db_fetch_assoc']($request))
780
	{
781
		// Censor....
782
		if (empty($row['body']))
783
			$row['body'] = '';
784
785
		$row['subject'] = $smcFunc['htmltrim']($row['subject']);
786
		if (empty($row['subject']))
787
			$row['subject'] = $txt['no_subject'];
788
789
		censorText($row['body']);
790
		censorText($row['subject']);
791
792
		// BBC-ilize the message.
793
		$row['body'] = parse_bbc($row['body'], true, 'draft' . $row['id_draft']);
794
795
		// Have they provide who this will go to?
796
		$recipients = array(
797
			'to' => array(),
798
			'bcc' => array(),
799
		);
800
		$recipient_ids = (!empty($row['to_list'])) ? $smcFunc['json_decode']($row['to_list'], true) : array();
801
802
		// @todo ... this is a bit ugly since it runs an extra query for every message, do we want this?
803
		// at least its only for draft PM's and only the user can see them ... so not heavily used .. still
804
		if (!empty($recipient_ids['to']) || !empty($recipient_ids['bcc']))
805
		{
806
			$recipient_ids['to'] = array_map('intval', $recipient_ids['to']);
807
			$recipient_ids['bcc'] = array_map('intval', $recipient_ids['bcc']);
808
			$allRecipients = array_merge($recipient_ids['to'], $recipient_ids['bcc']);
809
810
			$request_2 = $smcFunc['db_query']('', '
811
				SELECT id_member, real_name
812
				FROM {db_prefix}members
813
				WHERE id_member IN ({array_int:member_list})',
814
				array(
815
					'member_list' => $allRecipients,
816
				)
817
			);
818
			while ($result = $smcFunc['db_fetch_assoc']($request_2))
819
			{
820
				$recipientType = in_array($result['id_member'], $recipient_ids['bcc']) ? 'bcc' : 'to';
821
				$recipients[$recipientType][] = $result['real_name'];
822
			}
823
			$smcFunc['db_free_result']($request_2);
824
		}
825
826
		// Add the items to the array for template use
827
		$context['drafts'][$counter += $reverse ? -1 : 1] = array(
828
			'body' => $row['body'],
829
			'counter' => $counter,
830
			'subject' => $row['subject'],
831
			'time' => timeformat($row['poster_time']),
832
			'timestamp' => forum_time(true, $row['poster_time']),
833
			'id_draft' => $row['id_draft'],
834
			'recipients' => $recipients,
835
			'age' => floor((time() - $row['poster_time']) / 86400),
836
			'remaining' => (!empty($modSettings['drafts_keep_days']) ? floor($modSettings['drafts_keep_days'] - ((time() - $row['poster_time']) / 86400)) : 0),
837
			'quickbuttons' => array(
838
				'edit' => array(
839
					'label' => $txt['draft_edit'],
840
					'href' => $scripturl.'?action=pm;sa=showpmdrafts;id_draft='.$row['id_draft'].';'.$context['session_var'].'='.$context['session_id'],
841
					'icon' => 'modify_button'
842
				),
843
				'delete' => array(
844
					'label' => $txt['draft_delete'],
845
					'href' => $scripturl.'?action=pm;sa=showpmdrafts;delete='.$row['id_draft'].';'.$context['session_var'].'='.$context['session_id'],
846
					'javascript' => 'data-confirm="'.$txt['draft_remove'].'?" class="you_sure"',
847
					'icon' => 'remove_button'
848
				),
849
			),
850
		);
851
	}
852
	$smcFunc['db_free_result']($request);
853
854
	// if the drafts were retrieved in reverse order, then put them in the right order again.
855
	if ($reverse)
856
		$context['drafts'] = array_reverse($context['drafts'], true);
857
858
	// off to the template we go
859
	$context['page_title'] = $txt['drafts'];
860
	$context['sub_template'] = 'showPMDrafts';
861
	$context['linktree'][] = array(
862
		'url' => $scripturl . '?action=pm;sa=showpmdrafts',
863
		'name' => $txt['drafts'],
864
	);
865
}
866
867
?>