Issues (1686)

sources/ElkArte/Controller/Draft.php (2 issues)

1
<?php
2
3
/**
4
 * Allow for the saving, retrieving, deleting and settings for the drafts
5
 *
6
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * @version 2.0 dev
11
 *
12
 */
13
14
namespace ElkArte\Controller;
15
16
use BBC\ParserWrapper;
17
use ElkArte\Exceptions\Exception;
18
use ElkArte\Helper\Util;
19
use ElkArte\Languages\Txt;
20
21
/**
22
 * Draft controller.
23
 * This class handles requests that allow for the saving,
24
 * retrieving, deleting and settings for the drafts functionality.
25
 */
26
class Draft extends Post
27
{
28
	/**
29
	 * The id of the member
30
	 */
31
	private $_memID = 0;
32
33
	/**
34
	 * This method is executed before any action handler.
35
	 * Loads language, common needed stuffs.
36
	 */
37 4
	public function pre_dispatch()
38
	{
39
		// Language and helper functions
40 4
		Txt::load('Drafts');
41 4
		require_once(SUBSDIR . '/Drafts.subs.php');
42 4
		require_once(SUBSDIR . '/Profile.subs.php');
43
44 4
		$this->_memID = currentMemberID();
45 4
	}
46
47
	/**
48
	 * @override
49
	 */
50 2
	public function action_post()
51
	{
52 2
		$this->action_index();
53 2
	}
54
55
	/**
56
	 * Default method, just forwards, if we ever get here.
57
	 *
58
	 * @see \ElkArte\AbstractController::action_index()
59
	 */
60 2
	public function action_index()
61
	{
62
		// Where do you want to go today? :P
63 2
		$this->action_showProfileDrafts();
64 2
	}
65
66
	/**
67
	 * Show all drafts of a given type by the current user
68
	 *
69
	 * What it does:
70
	 *
71
	 * - Allows for the deleting and loading/editing of drafts
72
	 *
73
	 * @uses the showdrafts template
74
	 *
75
	 */
76 2
	public function action_showProfileDrafts()
77
	{
78 2
		global $txt, $modSettings, $context;
79
80
		// Safe is safe.
81 2
		if ($this->_memID !== $this->user->id)
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
82
		{
83
			throw new Exception('no_access', false);
84
		}
85
86 2
		require_once(SUBSDIR . '/Drafts.subs.php');
87
88
		// Some initial context.
89 2
		$context['start'] = $this->_req->getQuery('start', 'intval', 0);
90 2
		$context['current_member'] = $this->_memID;
91
92
		// If just deleting a draft, do it and then redirect back.
93 2
		if (!empty($this->_req->query->delete) || !empty($this->_req->post->delete))
94
		{
95
			$this->_action_delete('action=profile;u=' . $this->_memID . ';area=showdrafts;start=' . $context['start']);
96
		}
97
98
		// Get things started
99 2
		$msgCount = draftsCount($this->_memID, 0);
100 2
		$maxIndex = (int) $modSettings['defaultMaxMessages'];
101 2
102
		// Make sure the starting place makes sense and construct our friend the page index.
103
		$context['page_index'] = constructPageIndex('{scripturl}?action=pm;sa=showpmdrafts', $context['start'], $msgCount, $maxIndex);
104 2
		$context['current_page'] = $context['start'] / $maxIndex;
105 2
106
		[$maxIndex, $reverse, $limit, $order] = $this->_query_limits($msgCount, $maxIndex);
107 2
		$user_drafts = load_user_drafts($this->_memID, 0, false, $order, $limit);
108 2
109
		// Start counting at the number of the first message displayed.
110
		$counter = $reverse ? $context['start'] + $maxIndex + 1 : $context['start'];
111 2
		$context['posts'] = [];
112 2
		foreach ($user_drafts as $row)
113 2
		{
114
			$this->_prepare_body_subject($row['body'], $row['subject'], $txt['drafts_none'], (bool) $row['smileys_enabled']);
115
116
			// And the data used by the template
117
			$context['drafts'][$counter += $reverse ? -1 : 1] = [
118
				'body' => $row['body'],
119
				'counter' => $counter,
120
				'board' => [
121
					'name' => $row['bname'],
122
					'id' => $row['id_board'],
123
					'link' => '<a href="' . getUrl('board', ['board' => $row['id_board'], 'start' => '0', 'name' => $row['bname']]) . '">' . $row['bname'] . '</a>',
124
				],
125
				'topic' => [
126
					'id' => $row['id_topic'],
127
					'link' => empty($row['id_topic']) ? $row['subject'] : '<a href="' . getUrl('topic', ['topic' => $row['id_topic'], 'start' => '0', 'subject' => $row['subject']]) . '">' . $row['subject'] . '</a>',
128
				],
129
				'subject' => $row['subject'],
130
				'time' => standardTime($row['poster_time']),
131
				'html_time' => htmlTime($row['poster_time']),
132
				'timestamp' => forum_time(true, $row['poster_time']),
133
				'icon' => $row['icon'],
134
				'id_draft' => $row['id_draft'],
135
				'locked' => $row['locked'],
136
				'sticky' => $row['is_sticky'],
137
				'age' => floor((time() - $row['poster_time']) / 86400),
138
				'remaining' => (empty($modSettings['drafts_keep_days']) ? 0 : round($modSettings['drafts_keep_days'] - ((time() - $row['poster_time']) / 86400))),
139
				'buttons' => [
140
					'inline_mod_check' => [
141
						'class' => 'inline_mod_check',
142
						'checkbox' => 'always',
143
						'value' => $row['id_draft'],
144
						'name' => 'delete',
145
						'enabled' => true,
146
					],
147
					'remove' => [
148
						'url' => getUrl('profile', ['action' => 'profile', 'area' => 'showdrafts', 'u' => $context['member']['id'], 'name' => $context['member']['name'], 'delete' => $row['id_draft'], 'start' => $context['start'], '{session_data}']),
149
						'text' => 'draft_delete',
150
						'icon' => 'delete',
151
						'enabled' => true,
152
						'custom' => 'onclick="return confirm(' . JavaScriptEscape($txt['draft_remove'] . '?') . ');"',
153
					],
154
					'edit' => [
155
						'url' => getUrl('action', ['action' => 'post', empty($row['id_topic']) ? 'board' : 'topic' => empty($row['id_topic']) ? $row['id_board'] . '.0' : $row['id_topic'] . '.0', 'id_draft' => $row['id_draft']]),
156
						'text' => 'draft_edit',
157
						'icon' => 'modify',
158
						'enabled' => true,
159
					],
160
				]
161 2
			];
162
		}
163
164
		// If the drafts were retrieved in reverse order, get them right again.
165
		if ($reverse)
166
		{
167 2
			$context['drafts'] = array_reverse($context['drafts'], true);
168 2
		}
169 2
170 2
		// Menu tab
171
		$context[$context['profile_menu_name']]['object']->prepareTabData([
172
			'title' => 'drafts_show',
173 2
			'class' => 'i-comments',
174 2
			'description' => 'drafts_show_desc',
175
		]);
176
		$context['sub_template'] = 'showDrafts';
177
	}
178
179
	/**
180
	 * Deletes drafts stored in the $_REQUEST['delete'] index.
181
	 * The function redirects to a selected location.
182
	 *
183
	 * @param string $redirect - The url to redirect to after the drafts have
184
	 * been deleted
185
	 */
186
	private function _action_delete($redirect = '')
187
	{
188
		checkSession(empty($this->_req->post) ? 'get' : '');
189
190
		// Lets see what we have been sent, one or many to delete
191
		$toDelete = [];
192
		if (!empty($this->_req->query->delete))
193
		{
194
			$toDelete[] = (int) $this->_req->query->delete;
195
		}
196
		else
197
		{
198
			$toDelete = array_map('intval', $this->_req->post->delete);
199
		}
200
201
		if (!empty($toDelete))
202
		{
203
			deleteDrafts($toDelete, $this->_memID);
204
		}
205
206
		redirectexit($redirect);
207
	}
208
209
	/**
210
	 * Calculates start and limit for the query, maxIndex for the counter and
211
	 * if the query is reversed or not
212
	 *
213
	 * @param int $msgCount - Total number of drafts
214
	 * @param int $maxIndex - The maximum number of messages
215
	 *
216
	 * @return array - an array consisting of: $maxIndex, $reverse, $limit, $order
217 4
	 */
218
	private function _query_limits($msgCount, $maxIndex)
219 4
	{
220
		global $context, $modSettings;
221
222 4
		// Reverse the query if we're past 50% of the total for better performance.
223 4
		$start = $context['start'];
224 4
		$reverse = $start > $msgCount / 2;
225
		if ($reverse)
226
		{
227
			$maxIndex = $msgCount < $context['start'] + $modSettings['defaultMaxMessages'] + 1 && $msgCount > $context['start'] ? $msgCount - $context['start'] : (int) $modSettings['defaultMaxMessages'];
228
			$start = $msgCount < $context['start'] + $modSettings['defaultMaxMessages'] + 1 || $msgCount < $context['start'] + $modSettings['defaultMaxMessages'] ? 0 : $msgCount - $context['start'] - $modSettings['defaultMaxMessages'];
229
		}
230
231 4
		// Find this user's drafts
232 4
		$limit = $start . ', ' . $maxIndex;
233
		$order = 'ud.poster_time ' . ($reverse ? 'ASC' : 'DESC');
234 4
235
		return [$maxIndex, $reverse, $limit, $order];
236
	}
237
238
	/**
239
	 * Prepares the body and the subject of the draft
240
	 *
241
	 * @param string $body - The body of the message, passed by-ref
242
	 * @param string $subject - The subject, passed by-ref
243
	 * @param string $default_subject - The default subject if $subject is empty
244
	 * @param bool $smiley_enabled - Is the smiley are enabled or not
245
	 */
246
	private function _prepare_body_subject(&$body, &$subject, $default_subject, $smiley_enabled = true)
247
	{
248
		// Cleanup...
249
		if (empty($body))
250
		{
251
			$body = '';
252
		}
253
254
		$subject = Util::htmltrim($subject);
255
		if (empty($subject))
256
		{
257
			$subject = $default_subject;
258
		}
259
260
		// Censor...
261
		$body = censor($body);
262
		$subject = censor($subject);
263
264
		// BBC-ilize the message.
265
		$parser = ParserWrapper::instance();
266
		$body = $parser->parseMessage($body, $smiley_enabled);
267
	}
268
269
	/**
270
	 * @override
271
	 */
272
	public function action_post2()
273
	{
274
		$this->action_index();
275
	}
276
277
	/**
278
	 * @override
279
	 */
280
	public function action_save()
281
	{
282
		$this->action_index();
283
	}
284
285
	/**
286
	 * Show all PM drafts of the current user
287
	 *
288
	 * What it does:
289
	 *
290
	 * - Allows for the deleting and loading/editing of PM drafts
291
	 *
292
	 * @uses the showPMDrafts template
293 2
	 */
294
	public function action_showPMDrafts()
295 2
	{
296
		global $txt, $modSettings, $context;
297 2
298
		require_once(SUBSDIR . '/Drafts.subs.php');
299
300 2
		// Quick check how we got here.
301
		if ($this->_memID !== $this->user->id)
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
302
		{
303
			throw new Exception('no_access', false);
304
		}
305
306
		// Set up what we will need
307 2
		$context['start'] = $this->_req->getQuery('start', 'intval', 0);
308
309
		// If just deleting a draft, do it and then redirect back.
310 2
		if (!empty($this->_req->query->delete) || !empty($this->_req->post->delete))
311
		{
312
			return $this->_action_delete('action=pm;sa=showpmdrafts;start=' . $context['start']);
313
		}
314
315
		// Perhaps a draft was selected for editing? if so pass this off
316 2
		if (!empty($this->_req->query->id_draft) && !empty($context['drafts_pm_save']))
317
		{
318
			checkSession('get');
319
			redirectexit('action=pm;sa=send;id_draft=' . $this->_req->getQuery('id_draft', 'intval'));
320
		}
321
322
		// Get the count of applicable drafts
323 2
		$msgCount = draftsCount($this->_memID, 1);
324 2
		$maxIndex = (int) $modSettings['defaultMaxMessages'];
325
326
		// Make sure the starting place makes sense and construct our friend the page index.
327 2
		$context['page_index'] = constructPageIndex('{scripturl}?action=pm;sa=showpmdrafts', $context['start'], $msgCount, $maxIndex);
328 2
		$context['current_page'] = $context['start'] / $maxIndex;
329
330 2
		[$maxIndex, $reverse, $limit, $order] = $this->_query_limits($msgCount, $maxIndex);
331 2
		$user_drafts = load_user_drafts($this->_memID, 1, false, $order, $limit);
332
333
		// Start counting at the number of the first message displayed.
334 2
		$counter = $reverse ? $context['start'] + $maxIndex + 1 : $context['start'];
335 2
		$context['posts'] = array();
336 2
		foreach ($user_drafts as $row)
337
		{
338
			$this->_prepare_body_subject($row['body'], $row['subject'], $txt['no_subject'], true);
339
340
			// Have they provided who this will go to?
341
			$recipients = array(
342
				'to' => array(),
343
				'bcc' => array(),
344
			);
345
			$recipient_ids = (empty($row['to_list'])) ? [] : Util::unserialize($row['to_list']);
346
347
			// Get nice names to show the user, the id's are not that great to see!
348
			if (!empty($recipient_ids['to']) || !empty($recipient_ids['bcc']))
349
			{
350
				$recipient_ids['to'] = array_map('intval', $recipient_ids['to']);
351
				$recipient_ids['bcc'] = array_map('intval', $recipient_ids['bcc']);
352
				$allRecipients = array_merge($recipient_ids['to'], $recipient_ids['bcc']);
353
				$recipients = draftsRecipients($allRecipients, $recipient_ids);
354
			}
355
356
			// Add the items to the array for template use
357
			$context['drafts'][$counter += $reverse ? -1 : 1] = [
358
				'body' => $row['body'],
359
				'counter' => $counter,
360
				'subject' => $row['subject'],
361
				'time' => standardTime($row['poster_time']),
362
				'html_time' => htmlTime($row['poster_time']),
363
				'timestamp' => forum_time(true, $row['poster_time']),
364
				'id_draft' => $row['id_draft'],
365
				'recipients' => $recipients,
366
				'age' => floor((time() - $row['poster_time']) / 86400),
367
				'remaining' => (empty($modSettings['drafts_keep_days']) ? 0 : floor($modSettings['drafts_keep_days'] - ((time() - $row['poster_time']) / 86400))),
368
				'buttons' => [
369
					'reply_button' => [
370
						'url' => getUrl('action', ['action' => 'pm', 'sa' => 'showpmdrafts', 'id_draft' => $row['id_draft'], '{session_data}']),
371
						'text' => 'draft_edit',
372
						'icon' => 'modify',
373 2
						'enabled' => true,
374
					],
375
					'remove_button' => [
376
						'url' => getUrl('action', ['action' => 'pm', 'sa' => 'showpmdrafts', 'delete' => $row['id_draft'], '{session_data}']),
377
						'text' => 'draft_delete',
378
						'icon' => 'delete',
379 2
						'enabled' => true,
380 2
					],
381 2
				]
382 2
			];
383 2
		}
384
385 2
		// If the drafts were retrieved in reverse order, then put them in the right order again.
386
		if ($reverse)
387
		{
388
			$context['drafts'] = array_reverse($context['drafts'], true);
389
		}
390
391
		// Off to the template we go
392
		$context['page_title'] = $txt['drafts'];
393
		$context['sub_template'] = 'showPMDrafts';
394
		$context['breadcrumbs'][] = [
395
			'url' => getUrl('action', ['action' => 'pm', 'sa' => 'showpmdrafts']),
396
			'name' => $txt['drafts'],
397
		];
398
	}
399
}
400