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 https://www.simplemachines.org |
||
11 | * @copyright 2022 Simple Machines and individual contributors |
||
12 | * @license https://www.simplemachines.org/about/smf/license.php BSD |
||
13 | * |
||
14 | * @version 2.1.0 |
||
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
![]() |
|||
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 | array( |
||
234 | 'id_reply' => $reply_id, |
||
235 | 'type' => 1, |
||
236 | 'poster_time' => time(), |
||
237 | 'subject' => $draft['subject'], |
||
238 | 'body' => $draft['body'], |
||
239 | 'id_pm_draft' => $id_pm_draft, |
||
240 | 'to_list' => $smcFunc['json_encode']($recipientList), |
||
241 | ) |
||
242 | ); |
||
243 | |||
244 | // some items to return to the form |
||
245 | $context['draft_saved'] = true; |
||
246 | $context['id_pm_draft'] = $id_pm_draft; |
||
247 | } |
||
248 | // otherwise creating a new PM draft. |
||
249 | else |
||
250 | { |
||
251 | $id_pm_draft = $smcFunc['db_insert']('', |
||
252 | '{db_prefix}user_drafts', |
||
253 | array( |
||
254 | 'id_reply' => 'int', |
||
255 | 'type' => 'int', |
||
256 | 'poster_time' => 'int', |
||
257 | 'id_member' => 'int', |
||
258 | 'subject' => 'string-255', |
||
259 | 'body' => 'string-65534', |
||
260 | 'to_list' => 'string-255', |
||
261 | ), |
||
262 | array( |
||
263 | $reply_id, |
||
264 | 1, |
||
265 | time(), |
||
266 | $user_info['id'], |
||
267 | $draft['subject'], |
||
268 | $draft['body'], |
||
269 | $smcFunc['json_encode']($recipientList), |
||
270 | ), |
||
271 | array( |
||
272 | 'id_draft' |
||
273 | ), |
||
274 | 1 |
||
275 | ); |
||
276 | |||
277 | // everything go as expected, if not toss back an error |
||
278 | if (!empty($id_pm_draft)) |
||
279 | { |
||
280 | $context['draft_saved'] = true; |
||
281 | $context['id_pm_draft'] = $id_pm_draft; |
||
282 | } |
||
283 | else |
||
284 | $post_errors[] = 'draft_not_saved'; |
||
285 | } |
||
286 | |||
287 | // if we were called from the autosave function, send something back |
||
288 | if (!empty($id_pm_draft) && isset($_REQUEST['xml']) && !in_array('session_timeout', $post_errors)) |
||
289 | { |
||
290 | $context['draft_saved_on'] = time(); |
||
291 | XmlDraft($id_pm_draft); |
||
292 | } |
||
293 | |||
294 | return; |
||
295 | } |
||
296 | |||
297 | /** |
||
298 | * Reads a draft in from the user_drafts table |
||
299 | * Validates that the draft is the user''s draft |
||
300 | * Optionally loads the draft in to context or superglobal for loading in to the form |
||
301 | * |
||
302 | * @param int $id_draft ID of the draft to load |
||
303 | * @param int $type Type of draft - 0 for post or 1 for PM |
||
304 | * @param boolean $check Validate that this draft belongs to the current user |
||
305 | * @param boolean $load Whether or not to load the data into variables for use on a form |
||
306 | * @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 |
||
307 | */ |
||
308 | function ReadDraft($id_draft, $type = 0, $check = true, $load = false) |
||
309 | { |
||
310 | global $context, $user_info, $smcFunc, $modSettings; |
||
311 | |||
312 | // like purell always clean to be sure |
||
313 | $id_draft = (int) $id_draft; |
||
314 | $type = (int) $type; |
||
315 | |||
316 | // nothing to read, nothing to do |
||
317 | if (empty($id_draft)) |
||
318 | return false; |
||
319 | |||
320 | // load in this draft from the DB |
||
321 | $request = $smcFunc['db_query']('', ' |
||
322 | SELECT is_sticky, locked, smileys_enabled, icon, body , subject, |
||
323 | id_board, id_draft, id_reply, to_list |
||
324 | FROM {db_prefix}user_drafts |
||
325 | WHERE id_draft = {int:id_draft}' . ($check ? ' |
||
326 | AND id_member = {int:id_member}' : '') . ' |
||
327 | AND type = {int:type}' . (!empty($modSettings['drafts_keep_days']) ? ' |
||
328 | AND poster_time > {int:time}' : '') . ' |
||
329 | LIMIT 1', |
||
330 | array( |
||
331 | 'id_member' => $user_info['id'], |
||
332 | 'id_draft' => $id_draft, |
||
333 | 'type' => $type, |
||
334 | 'time' => (!empty($modSettings['drafts_keep_days']) ? (time() - ($modSettings['drafts_keep_days'] * 86400)) : 0), |
||
335 | ) |
||
336 | ); |
||
337 | |||
338 | // no results? |
||
339 | if (!$smcFunc['db_num_rows']($request)) |
||
340 | return false; |
||
341 | |||
342 | // load up the data |
||
343 | $draft_info = $smcFunc['db_fetch_assoc']($request); |
||
344 | $smcFunc['db_free_result']($request); |
||
345 | |||
346 | // Load it up for the templates as well |
||
347 | if (!empty($load)) |
||
348 | { |
||
349 | if ($type === 0) |
||
350 | { |
||
351 | // a standard post draft? |
||
352 | $context['sticky'] = !empty($draft_info['is_sticky']) ? $draft_info['is_sticky'] : ''; |
||
353 | $context['locked'] = !empty($draft_info['locked']) ? $draft_info['locked'] : ''; |
||
354 | $context['use_smileys'] = !empty($draft_info['smileys_enabled']) ? true : false; |
||
355 | $context['icon'] = !empty($draft_info['icon']) ? $draft_info['icon'] : 'xx'; |
||
356 | $context['message'] = !empty($draft_info['body']) ? str_replace('<br>', "\n", un_htmlspecialchars(stripslashes($draft_info['body']))) : ''; |
||
357 | $context['subject'] = !empty($draft_info['subject']) ? stripslashes($draft_info['subject']) : ''; |
||
358 | $context['board'] = !empty($draft_info['id_board']) ? $draft_info['id_board'] : ''; |
||
359 | $context['id_draft'] = !empty($draft_info['id_draft']) ? $draft_info['id_draft'] : 0; |
||
360 | } |
||
361 | elseif ($type === 1) |
||
362 | { |
||
363 | // one of those pm drafts? then set it up like we have an error |
||
364 | $_REQUEST['subject'] = !empty($draft_info['subject']) ? stripslashes($draft_info['subject']) : ''; |
||
365 | $_REQUEST['message'] = !empty($draft_info['body']) ? str_replace('<br>', "\n", un_htmlspecialchars(stripslashes($draft_info['body']))) : ''; |
||
366 | $_REQUEST['replied_to'] = !empty($draft_info['id_reply']) ? $draft_info['id_reply'] : 0; |
||
367 | $context['id_pm_draft'] = !empty($draft_info['id_draft']) ? $draft_info['id_draft'] : 0; |
||
368 | $recipients = $smcFunc['json_decode']($draft_info['to_list'], true); |
||
369 | |||
370 | // make sure we only have integers in this array |
||
371 | $recipients['to'] = array_map('intval', $recipients['to']); |
||
372 | $recipients['bcc'] = array_map('intval', $recipients['bcc']); |
||
373 | |||
374 | // pretend we messed up to populate the pm message form |
||
375 | messagePostError(array(), array(), $recipients); |
||
376 | return true; |
||
377 | } |
||
378 | } |
||
379 | |||
380 | return $draft_info; |
||
381 | } |
||
382 | |||
383 | /** |
||
384 | * Deletes one or many drafts from the DB |
||
385 | * Validates the drafts are from the user |
||
386 | * is supplied an array of drafts will attempt to remove all of them |
||
387 | * |
||
388 | * @param int $id_draft The ID of the draft to delete |
||
389 | * @param boolean $check Whether or not to check that the draft belongs to the current user |
||
390 | * @return boolean False if it couldn't be deleted (doesn't return anything otherwise) |
||
391 | */ |
||
392 | function DeleteDraft($id_draft, $check = true) |
||
393 | { |
||
394 | global $user_info, $smcFunc; |
||
395 | |||
396 | // Only a single draft. |
||
397 | if (is_numeric($id_draft)) |
||
398 | $id_draft = array($id_draft); |
||
399 | |||
400 | // can't delete nothing |
||
401 | if (empty($id_draft) || ($check && empty($user_info['id']))) |
||
402 | return false; |
||
403 | |||
404 | $smcFunc['db_query']('', ' |
||
405 | DELETE FROM {db_prefix}user_drafts |
||
406 | WHERE id_draft IN ({array_int:id_draft})' . ($check ? ' |
||
407 | AND id_member = {int:id_member}' : ''), |
||
408 | array( |
||
409 | 'id_draft' => $id_draft, |
||
410 | 'id_member' => empty($user_info['id']) ? -1 : $user_info['id'], |
||
411 | ) |
||
412 | ); |
||
413 | } |
||
414 | |||
415 | /** |
||
416 | * Loads in a group of drafts for the user of a given type (0/posts, 1/pm's) |
||
417 | * loads a specific draft for forum use if selected. |
||
418 | * Used in the posting screens to allow draft selection |
||
419 | * Will load a draft if selected is supplied via post |
||
420 | * |
||
421 | * @param int $member_id ID of the member to show drafts for |
||
422 | * @param boolean|integer $topic If $type is 1, this can be set to only load drafts for posts in the specific topic |
||
423 | * @param int $draft_type The type of drafts to show - 0 for post drafts, 1 for PM drafts |
||
424 | * @return boolean False if the drafts couldn't be loaded, nothing otherwise |
||
425 | */ |
||
426 | function ShowDrafts($member_id, $topic = false, $draft_type = 0) |
||
427 | { |
||
428 | global $smcFunc, $scripturl, $context, $txt, $modSettings; |
||
429 | |||
430 | // Permissions |
||
431 | if (($draft_type === 0 && empty($context['drafts_save'])) || ($draft_type === 1 && empty($context['drafts_pm_save'])) || empty($member_id)) |
||
432 | return false; |
||
433 | |||
434 | $context['drafts'] = array(); |
||
435 | |||
436 | // has a specific draft has been selected? Load it up if there is not a message already in the editor |
||
437 | if (isset($_REQUEST['id_draft']) && empty($_POST['subject']) && empty($_POST['message'])) |
||
438 | ReadDraft((int) $_REQUEST['id_draft'], $draft_type, true, true); |
||
439 | |||
440 | // load the drafts this user has available |
||
441 | $request = $smcFunc['db_query']('', ' |
||
442 | SELECT subject, poster_time, id_board, id_topic, id_draft |
||
443 | FROM {db_prefix}user_drafts |
||
444 | WHERE id_member = {int:id_member}' . ((!empty($topic) && empty($draft_type)) ? ' |
||
445 | AND id_topic = {int:id_topic}' : (!empty($topic) ? ' |
||
446 | AND id_reply = {int:id_topic}' : '')) . ' |
||
447 | AND type = {int:draft_type}' . (!empty($modSettings['drafts_keep_days']) ? ' |
||
448 | AND poster_time > {int:time}' : '') . ' |
||
449 | ORDER BY poster_time DESC', |
||
450 | array( |
||
451 | 'id_member' => $member_id, |
||
452 | 'id_topic' => (int) $topic, |
||
453 | 'draft_type' => $draft_type, |
||
454 | 'time' => (!empty($modSettings['drafts_keep_days']) ? (time() - ($modSettings['drafts_keep_days'] * 86400)) : 0), |
||
455 | ) |
||
456 | ); |
||
457 | |||
458 | // add them to the draft array for display |
||
459 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
460 | { |
||
461 | if (empty($row['subject'])) |
||
462 | $row['subject'] = $txt['no_subject']; |
||
463 | |||
464 | // Post drafts |
||
465 | if ($draft_type === 0) |
||
466 | { |
||
467 | $tmp_subject = shorten_subject(stripslashes($row['subject']), 24); |
||
468 | $context['drafts'][] = array( |
||
469 | 'subject' => censorText($tmp_subject), |
||
470 | 'poster_time' => timeformat($row['poster_time']), |
||
471 | '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>', |
||
472 | ); |
||
473 | } |
||
474 | // PM drafts |
||
475 | elseif ($draft_type === 1) |
||
476 | { |
||
477 | $tmp_subject = shorten_subject(stripslashes($row['subject']), 24); |
||
478 | $context['drafts'][] = array( |
||
479 | 'subject' => censorText($tmp_subject), |
||
480 | 'poster_time' => timeformat($row['poster_time']), |
||
481 | 'link' => '<a href="' . $scripturl . '?action=pm;sa=send;id_draft=' . $row['id_draft'] . '">' . (!empty($row['subject']) ? $row['subject'] : $txt['drafts_none']) . '</a>', |
||
482 | ); |
||
483 | } |
||
484 | } |
||
485 | $smcFunc['db_free_result']($request); |
||
486 | } |
||
487 | |||
488 | /** |
||
489 | * Returns an xml response to an autosave ajax request |
||
490 | * provides the id of the draft saved and the time it was saved |
||
491 | * |
||
492 | * @param int $id_draft |
||
493 | */ |
||
494 | function XmlDraft($id_draft) |
||
495 | { |
||
496 | global $txt, $context; |
||
497 | |||
498 | header('content-type: text/xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set'])); |
||
499 | |||
500 | echo '<?xml version="1.0" encoding="', $context['character_set'], '"?> |
||
501 | <drafts> |
||
502 | <draft id="', $id_draft, '"><![CDATA[', $txt['draft_saved_on'], ': ', timeformat($context['draft_saved_on']), ']]></draft> |
||
503 | </drafts>'; |
||
504 | |||
505 | obExit(false); |
||
506 | } |
||
507 | |||
508 | /** |
||
509 | * Show all drafts of a given type by the current user |
||
510 | * Uses the showdraft template |
||
511 | * Allows for the deleting and loading/editing of drafts |
||
512 | * |
||
513 | * @param int $memID |
||
514 | * @param int $draft_type |
||
515 | */ |
||
516 | function showProfileDrafts($memID, $draft_type = 0) |
||
517 | { |
||
518 | global $txt, $scripturl, $modSettings, $context, $smcFunc, $options; |
||
519 | |||
520 | // Some initial context. |
||
521 | $context['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0; |
||
522 | $context['current_member'] = $memID; |
||
523 | |||
524 | // If just deleting a draft, do it and then redirect back. |
||
525 | if (!empty($_REQUEST['delete'])) |
||
526 | { |
||
527 | checkSession('get'); |
||
528 | $id_delete = (int) $_REQUEST['delete']; |
||
529 | |||
530 | $smcFunc['db_query']('', ' |
||
531 | DELETE FROM {db_prefix}user_drafts |
||
532 | WHERE id_draft = {int:id_draft} |
||
533 | AND id_member = {int:id_member} |
||
534 | AND type = {int:draft_type}', |
||
535 | array( |
||
536 | 'id_draft' => $id_delete, |
||
537 | 'id_member' => $memID, |
||
538 | 'draft_type' => $draft_type, |
||
539 | ) |
||
540 | ); |
||
541 | |||
542 | redirectexit('action=profile;u=' . $memID . ';area=showdrafts;start=' . $context['start']); |
||
543 | } |
||
544 | |||
545 | // Default to 10. |
||
546 | if (empty($_REQUEST['viewscount']) || !is_numeric($_REQUEST['viewscount'])) |
||
547 | $_REQUEST['viewscount'] = 10; |
||
548 | |||
549 | // Get the count of applicable drafts on the boards they can (still) see ... |
||
550 | // @todo .. should we just let them see their drafts even if they have lost board access ? |
||
551 | $request = $smcFunc['db_query']('', ' |
||
552 | SELECT COUNT(*) |
||
553 | FROM {db_prefix}user_drafts AS ud |
||
554 | INNER JOIN {db_prefix}boards AS b ON (b.id_board = ud.id_board AND {query_see_board}) |
||
555 | WHERE id_member = {int:id_member} |
||
556 | AND type={int:draft_type}' . (!empty($modSettings['drafts_keep_days']) ? ' |
||
557 | AND poster_time > {int:time}' : ''), |
||
558 | array( |
||
559 | 'id_member' => $memID, |
||
560 | 'draft_type' => $draft_type, |
||
561 | 'time' => (!empty($modSettings['drafts_keep_days']) ? (time() - ($modSettings['drafts_keep_days'] * 86400)) : 0), |
||
562 | ) |
||
563 | ); |
||
564 | list ($msgCount) = $smcFunc['db_fetch_row']($request); |
||
565 | $smcFunc['db_free_result']($request); |
||
566 | |||
567 | $maxPerPage = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages']; |
||
568 | $maxIndex = $maxPerPage; |
||
569 | |||
570 | // Make sure the starting place makes sense and construct our friend the page index. |
||
571 | $context['page_index'] = constructPageIndex($scripturl . '?action=profile;u=' . $memID . ';area=showdrafts', $context['start'], $msgCount, $maxIndex); |
||
572 | $context['current_page'] = $context['start'] / $maxIndex; |
||
573 | |||
574 | // Reverse the query if we're past 50% of the pages for better performance. |
||
575 | $start = $context['start']; |
||
576 | $reverse = $_REQUEST['start'] > $msgCount / 2; |
||
577 | if ($reverse) |
||
578 | { |
||
579 | $maxIndex = $msgCount < $context['start'] + $maxPerPage + 1 && $msgCount > $context['start'] ? $msgCount - $context['start'] : $maxPerPage; |
||
580 | $start = $msgCount < $context['start'] + $maxPerPage + 1 || $msgCount < $context['start'] + $maxPerPage ? 0 : $msgCount - $context['start'] - $maxPerPage; |
||
581 | } |
||
582 | |||
583 | // Find this user's drafts for the boards they can access |
||
584 | // @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 |
||
585 | // access to the board or if the topic moves to a board they can not see? |
||
586 | $request = $smcFunc['db_query']('', ' |
||
587 | SELECT |
||
588 | b.id_board, b.name AS bname, |
||
589 | 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 |
||
590 | FROM {db_prefix}user_drafts AS ud |
||
591 | INNER JOIN {db_prefix}boards AS b ON (b.id_board = ud.id_board AND {query_see_board}) |
||
592 | WHERE ud.id_member = {int:current_member} |
||
593 | AND type = {int:draft_type}' . (!empty($modSettings['drafts_keep_days']) ? ' |
||
594 | AND poster_time > {int:time}' : '') . ' |
||
595 | ORDER BY ud.id_draft ' . ($reverse ? 'ASC' : 'DESC') . ' |
||
596 | LIMIT {int:start}, {int:max}', |
||
597 | array( |
||
598 | 'current_member' => $memID, |
||
599 | 'draft_type' => $draft_type, |
||
600 | 'time' => (!empty($modSettings['drafts_keep_days']) ? (time() - ($modSettings['drafts_keep_days'] * 86400)) : 0), |
||
601 | 'start' => $start, |
||
602 | 'max' => $maxIndex, |
||
603 | ) |
||
604 | ); |
||
605 | |||
606 | // Start counting at the number of the first message displayed. |
||
607 | $counter = $reverse ? $context['start'] + $maxIndex + 1 : $context['start']; |
||
608 | $context['posts'] = array(); |
||
609 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
610 | { |
||
611 | // Censor.... |
||
612 | if (empty($row['body'])) |
||
613 | $row['body'] = ''; |
||
614 | |||
615 | $row['subject'] = $smcFunc['htmltrim']($row['subject']); |
||
616 | if (empty($row['subject'])) |
||
617 | $row['subject'] = $txt['no_subject']; |
||
618 | |||
619 | censorText($row['body']); |
||
620 | censorText($row['subject']); |
||
621 | |||
622 | // BBC-ilize the message. |
||
623 | $row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], 'draft' . $row['id_draft']); |
||
624 | |||
625 | // And the array... |
||
626 | $context['drafts'][$counter += $reverse ? -1 : 1] = array( |
||
627 | 'body' => $row['body'], |
||
628 | 'counter' => $counter, |
||
629 | 'board' => array( |
||
630 | 'name' => $row['bname'], |
||
631 | 'id' => $row['id_board'] |
||
632 | ), |
||
633 | 'topic' => array( |
||
634 | 'id' => $row['id_topic'], |
||
635 | 'link' => empty($row['id']) ? $row['subject'] : '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['subject'] . '</a>', |
||
636 | ), |
||
637 | 'subject' => $row['subject'], |
||
638 | 'time' => timeformat($row['poster_time']), |
||
639 | 'timestamp' => $row['poster_time'], |
||
640 | 'icon' => $row['icon'], |
||
641 | 'id_draft' => $row['id_draft'], |
||
642 | 'locked' => $row['locked'], |
||
643 | 'sticky' => $row['is_sticky'], |
||
644 | 'quickbuttons' => array( |
||
645 | 'edit' => array( |
||
646 | 'label' => $txt['draft_edit'], |
||
647 | 'href' => $scripturl.'?action=post;'.(empty($row['id_topic']) ? 'board='.$row['id_board'] : 'topic='.$row['id_topic']).'.0;id_draft='.$row['id_draft'], |
||
648 | 'icon' => 'modify_button' |
||
649 | ), |
||
650 | 'delete' => array( |
||
651 | 'label' => $txt['draft_delete'], |
||
652 | 'href' => $scripturl.'?action=profile;u='.$context['member']['id'].';area=showdrafts;delete='.$row['id_draft'].';'.$context['session_var'].'='.$context['session_id'], |
||
653 | 'javascript' => 'data-confirm="'.$txt['draft_remove'].'"', |
||
654 | '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' => 'main_icons drafts' |
||
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 | $context['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0; |
||
689 | |||
690 | // If just deleting a draft, do it and then redirect back. |
||
691 | if (!empty($_REQUEST['delete'])) |
||
692 | { |
||
693 | checkSession('get'); |
||
694 | $id_delete = (int) $_REQUEST['delete']; |
||
695 | $start = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0; |
||
696 | |||
697 | $smcFunc['db_query']('', ' |
||
698 | DELETE FROM {db_prefix}user_drafts |
||
699 | WHERE id_draft = {int:id_draft} |
||
700 | AND id_member = {int:id_member} |
||
701 | AND type = {int:draft_type}', |
||
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(*) |
||
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' => $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'].'?"', |
||
847 | 'class' => 'you_sure', |
||
848 | 'icon' => 'remove_button' |
||
849 | ), |
||
850 | ), |
||
851 | ); |
||
852 | } |
||
853 | $smcFunc['db_free_result']($request); |
||
854 | |||
855 | // if the drafts were retrieved in reverse order, then put them in the right order again. |
||
856 | if ($reverse) |
||
857 | $context['drafts'] = array_reverse($context['drafts'], true); |
||
858 | |||
859 | // off to the template we go |
||
860 | $context['page_title'] = $txt['drafts']; |
||
861 | $context['sub_template'] = 'showPMDrafts'; |
||
862 | $context['linktree'][] = array( |
||
863 | 'url' => $scripturl . '?action=pm;sa=showpmdrafts', |
||
864 | 'name' => $txt['drafts'], |
||
865 | ); |
||
866 | } |
||
867 | |||
868 | ?> |