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']); |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
![]() |
|||||
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); |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
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)) |
||||
0 ignored issues
–
show
$post_errors of type string is incompatible with the type array expected by parameter $haystack of in_array() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
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)) |
||||
0 ignored issues
–
show
|
|||||
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 | ?> |