|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/** |
|
4
|
|
|
* This file is mainly meant for controlling the actions related to personal |
|
5
|
|
|
* messages. It allows viewing, sending, deleting, and marking. |
|
6
|
|
|
* For compatibility reasons, they are often called "instant messages". |
|
7
|
|
|
* |
|
8
|
|
|
* @package ElkArte Forum |
|
9
|
|
|
* @copyright ElkArte Forum contributors |
|
10
|
|
|
* @license BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file) |
|
11
|
|
|
* |
|
12
|
|
|
* This file contains code covered by: |
|
13
|
|
|
* copyright: 2011 Simple Machines (http://www.simplemachines.org) |
|
14
|
|
|
* |
|
15
|
|
|
* @version 2.0 Beta 1 |
|
16
|
|
|
* |
|
17
|
|
|
*/ |
|
18
|
|
|
|
|
19
|
|
|
namespace ElkArte\PersonalMessage; |
|
20
|
|
|
|
|
21
|
|
|
use BBC\ParserWrapper; |
|
22
|
|
|
use ElkArte\AbstractController; |
|
23
|
|
|
use ElkArte\Action; |
|
24
|
|
|
use ElkArte\Cache\Cache; |
|
25
|
|
|
use ElkArte\Errors\ErrorContext; |
|
26
|
|
|
use ElkArte\Exceptions\ControllerRedirectException; |
|
27
|
|
|
use ElkArte\Exceptions\Exception; |
|
28
|
|
|
use ElkArte\Exceptions\PmErrorException; |
|
29
|
|
|
use ElkArte\Helper\Util; |
|
30
|
|
|
use ElkArte\Helper\ValuesContainer; |
|
31
|
|
|
use ElkArte\Languages\Txt; |
|
32
|
|
|
use ElkArte\MembersList; |
|
33
|
|
|
use ElkArte\MessagesCallback\BodyParser\Normal; |
|
34
|
|
|
use ElkArte\MessagesCallback\PmRenderer; |
|
35
|
|
|
use ElkArte\Profile\Profile; |
|
36
|
|
|
use ElkArte\Profile\ProfileOptions; |
|
37
|
|
|
use ElkArte\User; |
|
38
|
|
|
use ElkArte\VerificationControls\VerificationControlsIntegrate; |
|
39
|
|
|
|
|
40
|
|
|
/** |
|
41
|
|
|
* Class PersonalMessage |
|
42
|
|
|
* |
|
43
|
|
|
* It allows viewing, sending, deleting, and marking personal messages |
|
44
|
|
|
* |
|
45
|
|
|
* @package ElkArte\PersonalMessage |
|
46
|
|
|
*/ |
|
47
|
|
|
class PersonalMessage extends AbstractController |
|
48
|
|
|
{ |
|
49
|
|
|
/** |
|
50
|
|
|
* This method is executed before any other in this file (when the class is |
|
51
|
|
|
* loaded by the dispatcher). |
|
52
|
|
|
* |
|
53
|
|
|
* What it does: |
|
54
|
|
|
* |
|
55
|
|
|
* - It sets the context, loads templates and language file(s), as necessary |
|
56
|
|
|
* for the function that will be called. |
|
57
|
|
|
*/ |
|
58
|
|
|
public function pre_dispatch() |
|
59
|
|
|
{ |
|
60
|
|
|
global $txt, $context, $modSettings; |
|
61
|
|
|
|
|
62
|
|
|
// No guests! |
|
63
|
|
|
is_not_guest(); |
|
64
|
|
|
|
|
65
|
|
|
// You're not supposed to be here at all if you can't even read PMs. |
|
66
|
|
|
isAllowedTo('pm_read'); |
|
67
|
|
|
|
|
68
|
|
|
// This file contains PM functions such as mark, send, delete |
|
69
|
|
|
require_once(SUBSDIR . '/PersonalMessage.subs.php'); |
|
70
|
|
|
|
|
71
|
|
|
// Templates, language, javascript |
|
72
|
|
|
Txt::load('PersonalMessage'); |
|
73
|
|
|
loadJavascriptFile(['suggest.js', 'PersonalMessage.js']); |
|
74
|
|
|
|
|
75
|
|
|
if ($this->getApi() === false) |
|
76
|
|
|
{ |
|
77
|
|
|
theme()->getTemplates()->load('PersonalMessage'); |
|
78
|
|
|
} |
|
79
|
|
|
|
|
80
|
|
|
$this->_events->trigger('pre_dispatch', ['xml' => $this->getApi() !== false]); |
|
81
|
|
|
|
|
82
|
|
|
// Load up the members' maximum message capacity. |
|
83
|
|
|
$this->_loadMessageLimit(); |
|
84
|
|
|
|
|
85
|
|
|
// A previous message was sent successfully? show a small indication. |
|
86
|
|
|
if ($this->_req->getQuery('done') === 'sent') |
|
87
|
|
|
{ |
|
88
|
|
|
$context['pm_sent'] = true; |
|
89
|
|
|
} |
|
90
|
|
|
|
|
91
|
|
|
// Load the label counts data. |
|
92
|
|
|
if (User::$settings['new_pm'] || !Cache::instance()->getVar($context['labels'], 'labelCounts:' . $this->user->id, 720)) |
|
|
|
|
|
|
93
|
|
|
{ |
|
94
|
|
|
$this->_loadLabels(); |
|
95
|
|
|
|
|
96
|
|
|
// Get the message count for each label |
|
97
|
|
|
$context['labels'] = loadPMLabels($context['labels']); |
|
98
|
|
|
} |
|
99
|
|
|
|
|
100
|
|
|
// Now we have the labels, and assuming we have unsorted mail, apply our rules! |
|
101
|
|
|
if (User::$settings['new_pm']) |
|
102
|
|
|
{ |
|
103
|
|
|
// Apply our rules to the new PM's |
|
104
|
|
|
applyRules(); |
|
105
|
|
|
|
|
106
|
|
|
require_once(SUBSDIR . '/Members.subs.php'); |
|
107
|
|
|
updateMemberData($this->user->id, ['new_pm' => 0]); |
|
108
|
|
|
|
|
109
|
|
|
// Turn the new PM's status off, for the popup alert, since they have entered the PM area |
|
110
|
|
|
toggleNewPM($this->user->id); |
|
111
|
|
|
} |
|
112
|
|
|
|
|
113
|
|
|
// This determines if we have more labels than just the standard inbox. |
|
114
|
|
|
$context['currently_using_labels'] = count($context['labels']) > 1 ? 1 : 0; |
|
115
|
|
|
|
|
116
|
|
|
// Some stuff for the labels... |
|
117
|
|
|
$label = $this->_req->getQuery('l', 'intval'); |
|
118
|
|
|
$folder = $this->_req->getQuery('f', 'trim', ''); |
|
119
|
|
|
$start = $this->_req->getQuery('start', 'trim'); |
|
120
|
|
|
$context['current_label_id'] = isset($label, $context['labels'][$label]) ? (int) $label : -1; |
|
121
|
|
|
$context['current_label'] = &$context['labels'][$context['current_label_id']]['name']; |
|
122
|
|
|
$context['folder'] = $folder !== 'sent' ? 'inbox' : 'sent'; |
|
123
|
|
|
|
|
124
|
|
|
// This is convenient. Do you know how annoying it is to do this every time?! |
|
125
|
|
|
$context['current_label_redirect'] = 'action=pm;f=' . $context['folder'] . (isset($start) ? ';start=' . $start : '') . (empty($label) ? '' : ';l=' . $label); |
|
126
|
|
|
$context['can_issue_warning'] = featureEnabled('w') && allowedTo('issue_warning') && !empty($modSettings['warning_enable']); |
|
127
|
|
|
|
|
128
|
|
|
// Build the breadcrumbs for all the actions... |
|
129
|
|
|
$context['breadcrumbs'][] = [ |
|
130
|
|
|
'url' => getUrl('action', ['action' => 'pm']), |
|
131
|
|
|
'name' => $txt['personal_messages'] |
|
132
|
|
|
]; |
|
133
|
|
|
|
|
134
|
|
|
// Preferences... |
|
135
|
|
|
$context['display_mode'] = PmHelper::getDisplayMode(); |
|
136
|
|
|
} |
|
137
|
|
|
|
|
138
|
|
|
/** |
|
139
|
|
|
* Load a members message limit and prepares the limit bar |
|
140
|
|
|
*/ |
|
141
|
|
|
private function _loadMessageLimit(): void |
|
142
|
|
|
{ |
|
143
|
|
|
global $context, $txt; |
|
144
|
|
|
|
|
145
|
|
|
$context['message_limit'] = loadMessageLimit(); |
|
146
|
|
|
|
|
147
|
|
|
// Prepare the context for the capacity bar. |
|
148
|
|
|
if (!empty($context['message_limit'])) |
|
149
|
|
|
{ |
|
150
|
|
|
$bar = ($this->user->messages * 100) / $context['message_limit']; |
|
|
|
|
|
|
151
|
|
|
|
|
152
|
|
|
$context['limit_bar'] = [ |
|
153
|
|
|
'messages' => $this->user->messages, |
|
154
|
|
|
'allowed' => $context['message_limit'], |
|
155
|
|
|
'percent' => $bar, |
|
156
|
|
|
'bar' => min(100, (int) $bar), |
|
157
|
|
|
'text' => sprintf($txt['pm_currently_using'], $this->user->messages, round($bar, 1)), |
|
158
|
|
|
]; |
|
159
|
|
|
} |
|
160
|
|
|
} |
|
161
|
|
|
|
|
162
|
|
|
/** |
|
163
|
|
|
* Loads the user defined label's for use in the template etc. |
|
164
|
|
|
*/ |
|
165
|
|
|
private function _loadLabels(): void |
|
166
|
|
|
{ |
|
167
|
|
|
global $context, $txt; |
|
168
|
|
|
|
|
169
|
|
|
$userLabels = explode(',', User::$settings['message_labels'] ?? ''); |
|
170
|
|
|
|
|
171
|
|
|
foreach ($userLabels as $id_label => $label_name) |
|
172
|
|
|
{ |
|
173
|
|
|
if (empty($label_name)) |
|
174
|
|
|
{ |
|
175
|
|
|
continue; |
|
176
|
|
|
} |
|
177
|
|
|
|
|
178
|
|
|
$context['labels'][$id_label] = [ |
|
179
|
|
|
'id' => $id_label, |
|
180
|
|
|
'name' => trim($label_name), |
|
181
|
|
|
'messages' => 0, |
|
182
|
|
|
'unread_messages' => 0, |
|
183
|
|
|
]; |
|
184
|
|
|
} |
|
185
|
|
|
|
|
186
|
|
|
// The default inbox is always available |
|
187
|
|
|
$context['labels'][-1] = [ |
|
188
|
|
|
'id' => -1, |
|
189
|
|
|
'name' => $txt['pm_msg_label_inbox'], |
|
190
|
|
|
'messages' => 0, |
|
191
|
|
|
'unread_messages' => 0, |
|
192
|
|
|
]; |
|
193
|
|
|
} |
|
194
|
|
|
|
|
195
|
|
|
/** |
|
196
|
|
|
* This is the main function of personal messages, called before the action handler. |
|
197
|
|
|
* |
|
198
|
|
|
* What it does: |
|
199
|
|
|
* |
|
200
|
|
|
* - PersonalMessages is a menu-based controller. |
|
201
|
|
|
* - It sets up the menu. |
|
202
|
|
|
* - Calls from the menu the appropriate method/function for the current area. |
|
203
|
|
|
* |
|
204
|
|
|
* @see AbstractController::action_index |
|
205
|
|
|
*/ |
|
206
|
|
|
public function action_index() |
|
207
|
|
|
{ |
|
208
|
|
|
global $context; |
|
209
|
|
|
|
|
210
|
|
|
// Finally, all the things we know how to do |
|
211
|
|
|
$subActions = [ |
|
212
|
|
|
'manlabels' => [$this, 'action_manlabels', 'permission' => 'pm_read'], |
|
213
|
|
|
'manrules' => [$this, 'action_manrules', 'permission' => 'pm_read'], |
|
214
|
|
|
'markunread' => [$this, 'action_markunread', 'permission' => 'pm_read'], |
|
215
|
|
|
'pmactions' => [$this, 'action_pmactions', 'permission' => 'pm_read'], |
|
216
|
|
|
'prune' => [$this, 'action_prune', 'permission' => 'pm_read'], |
|
217
|
|
|
'removeall' => [$this, 'action_removeall', 'permission' => 'pm_read'], |
|
218
|
|
|
'removeall2' => [$this, 'action_removeall2', 'permission' => 'pm_read'], |
|
219
|
|
|
'report' => [$this, 'action_report', 'permission' => 'pm_read'], |
|
220
|
|
|
'search' => [$this, 'action_search', 'permission' => 'pm_read'], |
|
221
|
|
|
'search2' => [$this, 'action_search2', 'permission' => 'pm_read'], |
|
222
|
|
|
'send' => [$this, 'action_send', 'permission' => 'pm_read'], |
|
223
|
|
|
'send2' => [$this, 'action_send2', 'permission' => 'pm_read'], |
|
224
|
|
|
'settings' => [$this, 'action_settings', 'permission' => 'pm_read'], |
|
225
|
|
|
'inbox' => [$this, 'action_folder', 'permission' => 'pm_read'], |
|
226
|
|
|
]; |
|
227
|
|
|
|
|
228
|
|
|
// Set up our action array |
|
229
|
|
|
$action = new Action('pm_index'); |
|
230
|
|
|
|
|
231
|
|
|
// Known action, go to it, otherwise the inbox for you |
|
232
|
|
|
$subAction = $action->initialize($subActions, 'inbox'); |
|
233
|
|
|
|
|
234
|
|
|
// Set the right index bar for the action |
|
235
|
|
|
if ($subAction === 'inbox') |
|
236
|
|
|
{ |
|
237
|
|
|
$this->_messageIndexBar($context['current_label_id'] === -1 ? $context['folder'] : 'label' . $context['current_label_id']); |
|
238
|
|
|
} |
|
239
|
|
|
elseif ($this->getApi() === false) |
|
240
|
|
|
{ |
|
241
|
|
|
$this->_messageIndexBar($subAction); |
|
242
|
|
|
} |
|
243
|
|
|
|
|
244
|
|
|
// And off we go! |
|
245
|
|
|
$action->dispatch($subAction); |
|
246
|
|
|
} |
|
247
|
|
|
|
|
248
|
|
|
/** |
|
249
|
|
|
* A menu to easily access different areas of the PM section |
|
250
|
|
|
* |
|
251
|
|
|
* @param string $area |
|
252
|
|
|
*/ |
|
253
|
|
|
private function _messageIndexBar($area): void |
|
254
|
|
|
{ |
|
255
|
|
|
global $txt, $context; |
|
256
|
|
|
|
|
257
|
|
|
require_once(SUBSDIR . '/Menu.subs.php'); |
|
258
|
|
|
|
|
259
|
|
|
$pm_areas = [ |
|
260
|
|
|
'folders' => [ |
|
261
|
|
|
'title' => $txt['pm_messages'], |
|
262
|
|
|
'counter' => 'unread_messages', |
|
263
|
|
|
'areas' => [ |
|
264
|
|
|
'inbox' => [ |
|
265
|
|
|
'label' => $txt['inbox'], |
|
266
|
|
|
'custom_url' => getUrl('action', ['action' => 'pm']), |
|
267
|
|
|
'counter' => 'unread_messages', |
|
268
|
|
|
], |
|
269
|
|
|
'send' => [ |
|
270
|
|
|
'label' => $txt['new_message'], |
|
271
|
|
|
'custom_url' => getUrl('action', ['action' => 'pm', 'sa' => 'send']), |
|
272
|
|
|
'permission' => 'pm_send', |
|
273
|
|
|
], |
|
274
|
|
|
'sent' => [ |
|
275
|
|
|
'label' => $txt['sent_items'], |
|
276
|
|
|
'custom_url' => getUrl('action', ['action' => 'pm', 'f' => 'sent']), |
|
277
|
|
|
], |
|
278
|
|
|
], |
|
279
|
|
|
], |
|
280
|
|
|
'labels' => [ |
|
281
|
|
|
'title' => $txt['pm_labels'], |
|
282
|
|
|
'counter' => 'labels_unread_total', |
|
283
|
|
|
'areas' => [], |
|
284
|
|
|
], |
|
285
|
|
|
'actions' => [ |
|
286
|
|
|
'title' => $txt['pm_actions'], |
|
287
|
|
|
'areas' => [ |
|
288
|
|
|
'search' => [ |
|
289
|
|
|
'label' => $txt['pm_search_bar_title'], |
|
290
|
|
|
'custom_url' => getUrl('action', ['action' => 'pm', 'sa' => 'search']), |
|
291
|
|
|
], |
|
292
|
|
|
'prune' => [ |
|
293
|
|
|
'label' => $txt['pm_prune'], |
|
294
|
|
|
'custom_url' => getUrl('action', ['action' => 'pm', 'sa' => 'prune']), |
|
295
|
|
|
], |
|
296
|
|
|
], |
|
297
|
|
|
], |
|
298
|
|
|
'pref' => [ |
|
299
|
|
|
'title' => $txt['pm_preferences'], |
|
300
|
|
|
'areas' => [ |
|
301
|
|
|
'manlabels' => [ |
|
302
|
|
|
'label' => $txt['pm_manage_labels'], |
|
303
|
|
|
'custom_url' => getUrl('action', ['action' => 'pm', 'sa' => 'manlabels']), |
|
304
|
|
|
], |
|
305
|
|
|
'manrules' => [ |
|
306
|
|
|
'label' => $txt['pm_manage_rules'], |
|
307
|
|
|
'custom_url' => getUrl('action', ['action' => 'pm', 'sa' => 'manrules']), |
|
308
|
|
|
], |
|
309
|
|
|
'settings' => [ |
|
310
|
|
|
'label' => $txt['pm_settings'], |
|
311
|
|
|
'custom_url' => getUrl('action', ['action' => 'pm', 'sa' => 'settings']), |
|
312
|
|
|
], |
|
313
|
|
|
], |
|
314
|
|
|
], |
|
315
|
|
|
]; |
|
316
|
|
|
|
|
317
|
|
|
// Handle labels. |
|
318
|
|
|
$label_counters = ['unread_messages' => $context['labels'][-1]['unread_messages']]; |
|
319
|
|
|
if (empty($context['currently_using_labels'])) |
|
320
|
|
|
{ |
|
321
|
|
|
unset($pm_areas['labels']); |
|
322
|
|
|
} |
|
323
|
|
|
else |
|
324
|
|
|
{ |
|
325
|
|
|
// Note we send labels by id as it will have fewer problems in the query string. |
|
326
|
|
|
$label_counters['labels_unread_total'] = 0; |
|
327
|
|
|
foreach ($context['labels'] as $label) |
|
328
|
|
|
{ |
|
329
|
|
|
if ($label['id'] === -1) |
|
330
|
|
|
{ |
|
331
|
|
|
continue; |
|
332
|
|
|
} |
|
333
|
|
|
|
|
334
|
|
|
// Count the number of unread items in labels. |
|
335
|
|
|
$label_counters['labels_unread_total'] += $label['unread_messages']; |
|
336
|
|
|
|
|
337
|
|
|
// Add the label to the menu. |
|
338
|
|
|
$pm_areas['labels']['areas']['label' . $label['id']] = [ |
|
339
|
|
|
'label' => $label['name'], |
|
340
|
|
|
'custom_url' => getUrl('action', ['action' => 'pm', 'l' => $label['id']]), |
|
341
|
|
|
'counter' => 'label' . $label['id'], |
|
342
|
|
|
'messages' => $label['messages'], |
|
343
|
|
|
]; |
|
344
|
|
|
|
|
345
|
|
|
$label_counters['label' . $label['id']] = $label['unread_messages']; |
|
346
|
|
|
} |
|
347
|
|
|
} |
|
348
|
|
|
|
|
349
|
|
|
// Do we have a limit on the number of messages we can keep? |
|
350
|
|
|
if (!empty($context['message_limit'])) |
|
351
|
|
|
{ |
|
352
|
|
|
$bar = round(($this->user->messages * 100) / $context['message_limit'], 1); |
|
|
|
|
|
|
353
|
|
|
|
|
354
|
|
|
$context['limit_bar'] = [ |
|
355
|
|
|
'messages' => $this->user->messages, |
|
356
|
|
|
'allowed' => $context['message_limit'], |
|
357
|
|
|
'percent' => $bar, |
|
358
|
|
|
'bar' => $bar > 100 ? 100 : (int) $bar, |
|
359
|
|
|
'text' => sprintf($txt['pm_currently_using'], $this->user->messages, $bar) |
|
360
|
|
|
]; |
|
361
|
|
|
} |
|
362
|
|
|
|
|
363
|
|
|
// Set a few options for the menu. |
|
364
|
|
|
$menuOptions = [ |
|
365
|
|
|
'current_area' => $area, |
|
366
|
|
|
'hook' => 'pm', |
|
367
|
|
|
'disable_url_session_check' => true, |
|
368
|
|
|
'counters' => empty($label_counters) ? 0 : $label_counters, |
|
369
|
|
|
]; |
|
370
|
|
|
|
|
371
|
|
|
// Actually create the menu! |
|
372
|
|
|
$pm_include_data = createMenu($pm_areas, $menuOptions); |
|
373
|
|
|
unset($pm_areas); |
|
374
|
|
|
|
|
375
|
|
|
// Make a note of the Unique ID for this menu. |
|
376
|
|
|
$context['pm_menu_id'] = $context['max_menu_id']; |
|
377
|
|
|
$context['pm_menu_name'] = 'menu_data_' . $context['pm_menu_id']; |
|
378
|
|
|
|
|
379
|
|
|
// Set the selected item. |
|
380
|
|
|
$context['menu_item_selected'] = $pm_include_data['current_area']; |
|
381
|
|
|
|
|
382
|
|
|
// Set the template for this area and add the profile layer. |
|
383
|
|
|
if ($this->getApi() === false) |
|
384
|
|
|
{ |
|
385
|
|
|
$template_layers = theme()->getLayers(); |
|
386
|
|
|
$template_layers->add('pm'); |
|
387
|
|
|
} |
|
388
|
|
|
} |
|
389
|
|
|
|
|
390
|
|
|
/** |
|
391
|
|
|
* Display a folder, i.e., inbox/sent etc. |
|
392
|
|
|
* |
|
393
|
|
|
* Display mode: 0 = all at once, 1 = one at a time, 2 = as a conversation |
|
394
|
|
|
* |
|
395
|
|
|
* @throws Exception |
|
396
|
|
|
* @uses subject_list, pm template layers |
|
397
|
|
|
* @uses folder sub template |
|
398
|
|
|
*/ |
|
399
|
|
|
public function action_folder(): void |
|
400
|
|
|
{ |
|
401
|
|
|
global $txt, $scripturl, $modSettings, $context, $subjects_request, $messages_request, $options; |
|
402
|
|
|
|
|
403
|
|
|
// Changing view? |
|
404
|
|
|
if ($this->_req->isSet('view')) |
|
405
|
|
|
{ |
|
406
|
|
|
$context['display_mode'] = (int) $context['display_mode'] > 1 ? 0 : (int) $context['display_mode'] + 1; |
|
407
|
|
|
require_once(SUBSDIR . '/Members.subs.php'); |
|
408
|
|
|
updateMemberData($this->user->id, ['pm_prefs' => (((int) User::$settings['pm_prefs']) & 252) | $context['display_mode']]); |
|
|
|
|
|
|
409
|
|
|
} |
|
410
|
|
|
|
|
411
|
|
|
// Make sure the starting location is valid. |
|
412
|
|
|
$start_raw = $this->_req->getQuery('start', 'trim'); |
|
413
|
|
|
if ($start_raw !== null && $start_raw !== 'new') |
|
414
|
|
|
{ |
|
415
|
|
|
$start = (int) $start_raw; |
|
416
|
|
|
} |
|
417
|
|
|
elseif ($start_raw === null && !empty($options['view_newest_pm_first'])) |
|
418
|
|
|
{ |
|
419
|
|
|
$start = 0; |
|
420
|
|
|
} |
|
421
|
|
|
else |
|
422
|
|
|
{ |
|
423
|
|
|
$start = 'new'; |
|
424
|
|
|
} |
|
425
|
|
|
|
|
426
|
|
|
// Set up some basic template stuff. |
|
427
|
|
|
$context['from_or_to'] = $context['folder'] !== 'sent' ? 'from' : 'to'; |
|
428
|
|
|
$context['signature_enabled'] = str_starts_with($modSettings['signature_settings'], '1'); |
|
429
|
|
|
$context['disabled_fields'] = isset($modSettings['disabled_profile_fields']) ? array_flip(explode(',', $modSettings['disabled_profile_fields'])) : []; |
|
430
|
|
|
|
|
431
|
|
|
// Set the template layers we need |
|
432
|
|
|
$template_layers = theme()->getLayers(); |
|
433
|
|
|
$template_layers->addAfter('subject_list', 'pm'); |
|
434
|
|
|
|
|
435
|
|
|
$labelQuery = $context['folder'] !== 'sent' ? ' |
|
436
|
|
|
AND FIND_IN_SET(' . $context['current_label_id'] . ', pmr.labels) != 0' : ''; |
|
437
|
|
|
|
|
438
|
|
|
// They didn't pick a sort, so we use the forum by default. |
|
439
|
|
|
$sort_by = $this->_req->getQuery('sort', 'trim', 'date'); |
|
440
|
|
|
$descending = $this->_req->hasQuery('desc'); |
|
441
|
|
|
|
|
442
|
|
|
// Set our sort by query. |
|
443
|
|
|
switch ($sort_by) |
|
444
|
|
|
{ |
|
445
|
|
|
case 'date': |
|
446
|
|
|
$sort_by_query = 'pm.id_pm'; |
|
447
|
|
|
if (!empty($options['view_newest_pm_first']) && !$this->_req->hasQuery('desc') && !$this->_req->hasQuery('asc')) |
|
448
|
|
|
{ |
|
449
|
|
|
$descending = true; |
|
450
|
|
|
} |
|
451
|
|
|
break; |
|
452
|
|
|
case 'name': |
|
453
|
|
|
$sort_by_query = "COALESCE(mem.real_name, '')"; |
|
454
|
|
|
break; |
|
455
|
|
|
case 'subject': |
|
456
|
|
|
$sort_by_query = 'pm.subject'; |
|
457
|
|
|
break; |
|
458
|
|
|
default: |
|
459
|
|
|
$sort_by_query = 'pm.id_pm'; |
|
460
|
|
|
} |
|
461
|
|
|
|
|
462
|
|
|
// Set the text to resemble the current folder. |
|
463
|
|
|
$pmbox = $context['folder'] !== 'sent' ? $txt['inbox'] : $txt['sent_items']; |
|
464
|
|
|
$txt['delete_all'] = str_replace('PMBOX', $pmbox, $txt['delete_all']); |
|
465
|
|
|
|
|
466
|
|
|
// Now, build the link tree! |
|
467
|
|
|
if ($context['current_label_id'] === -1) |
|
468
|
|
|
{ |
|
469
|
|
|
$context['breadcrumbs'][] = [ |
|
470
|
|
|
'url' => getUrl('action', ['action' => 'pm', 'f' => $context['folder']]), |
|
471
|
|
|
'name' => $pmbox |
|
472
|
|
|
]; |
|
473
|
|
|
} |
|
474
|
|
|
|
|
475
|
|
|
// Build it further if we also have a label. |
|
476
|
|
|
if ($context['current_label_id'] !== -1) |
|
477
|
|
|
{ |
|
478
|
|
|
$context['breadcrumbs'][] = [ |
|
479
|
|
|
'url' => getUrl('action', ['action' => 'pm', 'f' => $context['folder'], 'l' => $context['current_label_id']]), |
|
480
|
|
|
'name' => $txt['pm_current_label'] . ': ' . $context['current_label'] |
|
481
|
|
|
]; |
|
482
|
|
|
} |
|
483
|
|
|
|
|
484
|
|
|
// Figure out how many messages there are. |
|
485
|
|
|
$max_messages = getPMCount(false, null, $labelQuery); |
|
486
|
|
|
|
|
487
|
|
|
// Only show the button if there are messages to delete. |
|
488
|
|
|
$context['show_delete'] = $max_messages > 0; |
|
489
|
|
|
|
|
490
|
|
|
// Start on the last page. |
|
491
|
|
|
if (!is_numeric($start) || $start >= $max_messages) |
|
492
|
|
|
{ |
|
493
|
|
|
$start = ($max_messages - 1) - (($max_messages - 1) % $modSettings['defaultMaxMessages']); |
|
494
|
|
|
} |
|
495
|
|
|
elseif ($start < 0) |
|
496
|
|
|
{ |
|
497
|
|
|
$start = 0; |
|
498
|
|
|
} |
|
499
|
|
|
|
|
500
|
|
|
// ... but wait - what if we want to start from a specific message? |
|
501
|
|
|
if ($this->_req->isSet('pmid')) |
|
502
|
|
|
{ |
|
503
|
|
|
$pmID = $this->_req->getQuery('pmid', 'intval', 0); |
|
504
|
|
|
|
|
505
|
|
|
// Make sure you have access to this PM. |
|
506
|
|
|
if (!isAccessiblePM($pmID, $context['folder'] === 'sent' ? 'outbox' : 'inbox')) |
|
507
|
|
|
{ |
|
508
|
|
|
throw new Exception('no_access', false); |
|
509
|
|
|
} |
|
510
|
|
|
|
|
511
|
|
|
$context['current_pm'] = $pmID; |
|
512
|
|
|
|
|
513
|
|
|
// With only one page of PM's we're gonna want page 1. |
|
514
|
|
|
if ($max_messages <= $modSettings['defaultMaxMessages']) |
|
515
|
|
|
{ |
|
516
|
|
|
$start = 0; |
|
517
|
|
|
} |
|
518
|
|
|
// If we pass kstart we assume we're in the right place. |
|
519
|
|
|
elseif (!$this->_req->isSet('kstart')) |
|
520
|
|
|
{ |
|
521
|
|
|
$start = getPMCount($descending, $pmID, $labelQuery); |
|
522
|
|
|
|
|
523
|
|
|
// To stop the page index's being abnormal, start the page on the page the message |
|
524
|
|
|
// would normally be located on... |
|
525
|
|
|
$start = $modSettings['defaultMaxMessages'] * (int) ($start / $modSettings['defaultMaxMessages']); |
|
526
|
|
|
} |
|
527
|
|
|
} |
|
528
|
|
|
|
|
529
|
|
|
// Sanitize and validate pmsg variable if set. |
|
530
|
|
|
if ($this->_req->isSet('pmsg')) |
|
531
|
|
|
{ |
|
532
|
|
|
$pmsg = $this->_req->getQuery('pmsg', 'intval', 0); |
|
533
|
|
|
|
|
534
|
|
|
if (!isAccessiblePM($pmsg, $context['folder'] === 'sent' ? 'outbox' : 'inbox')) |
|
535
|
|
|
{ |
|
536
|
|
|
throw new Exception('no_access', false); |
|
537
|
|
|
} |
|
538
|
|
|
} |
|
539
|
|
|
|
|
540
|
|
|
// Determine the navigation context |
|
541
|
|
|
$context['links'] += [ |
|
542
|
|
|
'prev' => $start >= $modSettings['defaultMaxMessages'] ? $scripturl . '?action=pm;start=' . ($start - $modSettings['defaultMaxMessages']) : '', |
|
543
|
|
|
'next' => $start + $modSettings['defaultMaxMessages'] < $max_messages ? $scripturl . '?action=pm;start=' . ($start + $modSettings['defaultMaxMessages']) : '', |
|
544
|
|
|
]; |
|
545
|
|
|
|
|
546
|
|
|
// We now know what they want, so let's fetch those PM's |
|
547
|
|
|
[$pms, $posters, $recipients, $lastData] = loadPMs([ |
|
548
|
|
|
'sort_by_query' => $sort_by_query, |
|
549
|
|
|
'display_mode' => $context['display_mode'], |
|
550
|
|
|
'sort_by' => $sort_by, |
|
551
|
|
|
'label_query' => $labelQuery, |
|
552
|
|
|
'pmsg' => isset($pmsg) ? (int) $pmsg : 0, |
|
553
|
|
|
'descending' => $descending, |
|
554
|
|
|
'start' => $start, |
|
555
|
|
|
'limit' => $modSettings['defaultMaxMessages'], |
|
556
|
|
|
'folder' => $context['folder'], |
|
557
|
|
|
'pmid' => $pmID ?? 0, |
|
558
|
|
|
], $this->user->id); |
|
559
|
|
|
|
|
560
|
|
|
// Make sure that we have been given a correct head pm id if we are in conversation mode |
|
561
|
|
|
if ($context['display_mode'] === PmHelper::DISPLAY_AS_CONVERSATION && !empty($pmID) && $pmID != $lastData['id']) |
|
562
|
|
|
{ |
|
563
|
|
|
throw new Exception('no_access', false); |
|
564
|
|
|
} |
|
565
|
|
|
|
|
566
|
|
|
// If loadPMs returned results, let's show the pm subject list |
|
567
|
|
|
if (!empty($pms)) |
|
568
|
|
|
{ |
|
569
|
|
|
// Tell the template if no pm has specifically been selected |
|
570
|
|
|
if (empty($pmID)) |
|
571
|
|
|
{ |
|
572
|
|
|
$context['current_pm'] = 0; |
|
573
|
|
|
} |
|
574
|
|
|
|
|
575
|
|
|
$display_pms = $context['display_mode'] === PmHelper::DISPLAY_ALL_AT_ONCE ? $pms : [$lastData['id']]; |
|
576
|
|
|
|
|
577
|
|
|
// At this point we know the main id_pm's. But if we are looking at conversations, we need |
|
578
|
|
|
// the PMs that make up the conversation |
|
579
|
|
|
if ($context['display_mode'] === PmHelper::DISPLAY_AS_CONVERSATION) |
|
580
|
|
|
{ |
|
581
|
|
|
[$display_pms, $posters] = loadConversationList($lastData['head'], $recipients, $context['folder']); |
|
582
|
|
|
|
|
583
|
|
|
// Conversation list may expose additional PM's being displayed |
|
584
|
|
|
$all_pms = array_unique(array_merge($pms, $display_pms)); |
|
585
|
|
|
|
|
586
|
|
|
// See if any of these 'listing' PMs are in a conversation thread that has unread entries |
|
587
|
|
|
$context['conversation_unread'] = loadConversationUnreadStatus($all_pms); |
|
588
|
|
|
} |
|
589
|
|
|
// This is pretty much EVERY pm! |
|
590
|
|
|
else |
|
591
|
|
|
{ |
|
592
|
|
|
$all_pms = array_unique(array_merge($pms, $display_pms)); |
|
593
|
|
|
} |
|
594
|
|
|
|
|
595
|
|
|
// Get recipients (don't include bcc-recipients for your inbox, you're not supposed to know :P). |
|
596
|
|
|
[$context['message_labels'], $context['message_replied'], $context['message_unread']] = loadPMRecipientInfo($all_pms, $recipients, $context['folder']); |
|
597
|
|
|
|
|
598
|
|
|
// Make sure we don't load any unnecessary data for one at a time mode |
|
599
|
|
|
if ($context['display_mode'] === PmHelper::DISPLAY_ONE_AT_TIME) |
|
600
|
|
|
{ |
|
601
|
|
|
foreach ($posters as $pm_key => $sender) |
|
602
|
|
|
{ |
|
603
|
|
|
if (!in_array($pm_key, $display_pms)) |
|
604
|
|
|
{ |
|
605
|
|
|
unset($posters[$pm_key]); |
|
606
|
|
|
} |
|
607
|
|
|
} |
|
608
|
|
|
} |
|
609
|
|
|
|
|
610
|
|
|
// Load some information about the message sender |
|
611
|
|
|
$posters = array_unique($posters); |
|
612
|
|
|
if (!empty($posters)) |
|
613
|
|
|
{ |
|
614
|
|
|
MembersList::load($posters); |
|
615
|
|
|
} |
|
616
|
|
|
|
|
617
|
|
|
// Always build the subject list request when we have PMs, so the subject list |
|
618
|
|
|
// renders in all modes (0: all-at-once, 1: one-at-a-time, 2: conversation). |
|
619
|
|
|
// This also prevents sharing a DB result between subject and message renderers. |
|
620
|
|
|
// Get the order right. |
|
621
|
|
|
$orderBy = []; |
|
622
|
|
|
foreach (array_reverse($pms) as $pm) |
|
623
|
|
|
{ |
|
624
|
|
|
$orderBy[] = 'pm.id_pm = ' . $pm; |
|
625
|
|
|
} |
|
626
|
|
|
|
|
627
|
|
|
// Separate query for these bits, the callback will use it as required |
|
628
|
|
|
$subjects_request = loadPMSubjectRequest($pms, $orderBy); |
|
629
|
|
|
|
|
630
|
|
|
// Execute the load message query if a message has been chosen and let |
|
631
|
|
|
// the callback fetch the results. Otherwise, just show the pm selection list |
|
632
|
|
|
if (empty($pmsg) && empty($pmID) && $context['display_mode'] !== PmHelper::DISPLAY_ALL_AT_ONCE) |
|
633
|
|
|
{ |
|
634
|
|
|
$messages_request = false; |
|
635
|
|
|
} |
|
636
|
|
|
else |
|
637
|
|
|
{ |
|
638
|
|
|
$messages_request = loadPMMessageRequest($display_pms, $sort_by_query, $sort_by, $descending, $context['display_mode'], $context['folder']); |
|
639
|
|
|
} |
|
640
|
|
|
} |
|
641
|
|
|
else |
|
642
|
|
|
{ |
|
643
|
|
|
$messages_request = false; |
|
644
|
|
|
} |
|
645
|
|
|
|
|
646
|
|
|
// Initialize the subject and message render callbacks |
|
647
|
|
|
$bodyParser = new Normal([], false); |
|
648
|
|
|
$opt = new ValuesContainer(['recipients' => $recipients]); |
|
649
|
|
|
$renderer = new PmRenderer($messages_request, $this->user, $bodyParser, $opt); |
|
650
|
|
|
$subject_renderer = new PmRenderer($subjects_request ?? $messages_request, $this->user, $bodyParser, $opt); |
|
651
|
|
|
|
|
652
|
|
|
// Subject and Message |
|
653
|
|
|
$context['get_pmessage'] = [$renderer, 'getContext']; |
|
654
|
|
|
$context['get_psubject'] = [$subject_renderer, 'getContext']; |
|
655
|
|
|
|
|
656
|
|
|
// Prepare some items for the template |
|
657
|
|
|
$context['topic_starter_id'] = 0; |
|
658
|
|
|
$context['can_send_pm'] = allowedTo('pm_send'); |
|
659
|
|
|
$context['can_send_email'] = allowedTo('send_email_to_members'); |
|
660
|
|
|
$context['sub_template'] = 'folder'; |
|
661
|
|
|
$context['page_title'] = $txt['pm_inbox']; |
|
662
|
|
|
$context['sort_direction'] = $descending ? 'down' : 'up'; |
|
663
|
|
|
$context['sort_by'] = $sort_by; |
|
664
|
|
|
|
|
665
|
|
|
if ($messages_request !== false && !empty($context['show_delete']) && $messages_request->hasResults()) |
|
666
|
|
|
{ |
|
667
|
|
|
theme()->getLayers()->addEnd('pm_pages_and_buttons'); |
|
668
|
|
|
} |
|
669
|
|
|
|
|
670
|
|
|
// Set up the page index. |
|
671
|
|
|
$label_for_index = $this->_req->getQuery('l', 'intval'); |
|
672
|
|
|
$context['page_index'] = constructPageIndex('{scripturl}?action=pm;f=' . $context['folder'] . ($label_for_index !== null ? ';l=' . (int) $label_for_index : '') . ';sort=' . $context['sort_by'] . ($descending ? ';desc' : ''), $start, $max_messages, $modSettings['defaultMaxMessages']); |
|
673
|
|
|
$context['start'] = $start; |
|
674
|
|
|
|
|
675
|
|
|
$context['pm_form_url'] = $scripturl . '?action=pm;sa=pmactions;' . ($context['display_mode'] === PmHelper::DISPLAY_AS_CONVERSATION ? 'conversation;' : '') . 'f=' . $context['folder'] . ';start=' . $context['start'] . ($context['current_label_id'] !== -1 ? ';l=' . $context['current_label_id'] : ''); |
|
676
|
|
|
|
|
677
|
|
|
// Finally, mark the relevant messages as read. |
|
678
|
|
|
if ($context['folder'] !== 'sent' && !empty($context['labels'][(int) $context['current_label_id']]['unread_messages'])) |
|
679
|
|
|
{ |
|
680
|
|
|
// If the display mode is "old sk00l" do them all... |
|
681
|
|
|
if ($context['display_mode'] === PmHelper::DISPLAY_ALL_AT_ONCE) |
|
682
|
|
|
{ |
|
683
|
|
|
markMessages(null, $context['current_label_id']); |
|
684
|
|
|
} |
|
685
|
|
|
// Otherwise do just the currently displayed ones! |
|
686
|
|
|
elseif (!empty($context['current_pm'])) |
|
687
|
|
|
{ |
|
688
|
|
|
markMessages($display_pms, $context['current_label_id']); |
|
|
|
|
|
|
689
|
|
|
} |
|
690
|
|
|
} |
|
691
|
|
|
|
|
692
|
|
|
// Build the conversation button array. |
|
693
|
|
|
if ($context['display_mode'] === PmHelper::DISPLAY_AS_CONVERSATION && !empty($context['current_pm'])) |
|
694
|
|
|
{ |
|
695
|
|
|
$context['conversation_buttons'] = [ |
|
696
|
|
|
'delete' => [ |
|
697
|
|
|
'text' => 'delete_conversation', |
|
698
|
|
|
'lang' => true, |
|
699
|
|
|
'url' => $scripturl . '?action=pm;sa=pmactions;pm_actions%5B' . $context['current_pm'] . '%5D=delete;conversation;f=' . $context['folder'] . ';start=' . $context['start'] . ($context['current_label_id'] !== -1 ? ';l=' . $context['current_label_id'] : '') . ';' . $context['session_var'] . '=' . $context['session_id'], |
|
700
|
|
|
'custom' => 'onclick="return confirm(\'' . addslashes($txt['remove_message']) . '?\');"' |
|
701
|
|
|
], |
|
702
|
|
|
]; |
|
703
|
|
|
|
|
704
|
|
|
// Allow mods to add additional buttons here |
|
705
|
|
|
call_integration_hook('integrate_conversation_buttons'); |
|
706
|
|
|
} |
|
707
|
|
|
} |
|
708
|
|
|
|
|
709
|
|
|
/** |
|
710
|
|
|
* Send a new personal message? |
|
711
|
|
|
* |
|
712
|
|
|
* @throws Exception pm_not_yours |
|
713
|
|
|
*/ |
|
714
|
|
|
public function action_send(): void |
|
715
|
|
|
{ |
|
716
|
|
|
global $txt, $modSettings, $context; |
|
717
|
|
|
|
|
718
|
|
|
// Load in some text and template dependencies |
|
719
|
|
|
Txt::load('PersonalMessage'); |
|
720
|
|
|
theme()->getTemplates()->load('PersonalMessage'); |
|
721
|
|
|
|
|
722
|
|
|
// Set the template we will use |
|
723
|
|
|
$context['sub_template'] = 'send'; |
|
724
|
|
|
|
|
725
|
|
|
// Extract out the spam settings - cause it's neat. |
|
726
|
|
|
[$modSettings['max_pm_recipients'], $modSettings['pm_posts_verification'], $modSettings['pm_posts_per_hour']] = explode(',', $modSettings['pm_spam_settings']); |
|
727
|
|
|
|
|
728
|
|
|
// Set up some items for the template |
|
729
|
|
|
$context['page_title'] = $txt['send_message']; |
|
730
|
|
|
$context['reply'] = $this->_req->hasQuery('pmsg') || $this->_req->hasQuery('quote'); |
|
731
|
|
|
|
|
732
|
|
|
// Check whether we've gone over the limit of messages we can send per hour. |
|
733
|
|
|
if (!empty($modSettings['pm_posts_per_hour']) |
|
734
|
|
|
&& !allowedTo(['admin_forum', 'moderate_forum', 'send_mail']) |
|
735
|
|
|
&& $this->user->mod_cache['bq'] === '0=1' |
|
|
|
|
|
|
736
|
|
|
&& $this->user->mod_cache['gq'] === '0=1') |
|
737
|
|
|
{ |
|
738
|
|
|
// How many messages did they send this last hour? |
|
739
|
|
|
$pmCount = pmCount($this->user->id, 3600); |
|
|
|
|
|
|
740
|
|
|
|
|
741
|
|
|
if (!empty($pmCount) && $pmCount >= $modSettings['pm_posts_per_hour']) |
|
742
|
|
|
{ |
|
743
|
|
|
throw new Exception('pm_too_many_per_hour', true, [$modSettings['pm_posts_per_hour']]); |
|
744
|
|
|
} |
|
745
|
|
|
} |
|
746
|
|
|
|
|
747
|
|
|
try |
|
748
|
|
|
{ |
|
749
|
|
|
$pmsg_event = $this->_req->getQuery('pmsg', 'intval'); |
|
750
|
|
|
$pmsg_event_quote = $this->_req->getQuery('quote', 'trim', ''); |
|
751
|
|
|
if ($pmsg_event !== null) |
|
752
|
|
|
{ |
|
753
|
|
|
$this->_events->trigger('before_set_context', ['pmsg' => $pmsg_event, 'quote' => $pmsg_event_quote]); |
|
754
|
|
|
} |
|
755
|
|
|
} |
|
756
|
|
|
catch (PmErrorException $pmErrorException) |
|
757
|
|
|
{ |
|
758
|
|
|
$this->messagePostError($pmErrorException->namedRecipientList, $pmErrorException->recipientList, $pmErrorException->msgOptions); |
|
759
|
|
|
return; |
|
760
|
|
|
} |
|
761
|
|
|
|
|
762
|
|
|
// Quoting / Replying to a message? |
|
763
|
|
|
if ($this->_req->hasQuery('pmsg')) |
|
764
|
|
|
{ |
|
765
|
|
|
$pmsg = $this->_req->getQuery('pmsg', 'intval'); |
|
766
|
|
|
|
|
767
|
|
|
// Make sure this is accessible (not deleted) |
|
768
|
|
|
if (!isAccessiblePM($pmsg)) |
|
769
|
|
|
{ |
|
770
|
|
|
throw new Exception('no_access', false); |
|
771
|
|
|
} |
|
772
|
|
|
|
|
773
|
|
|
// Validate that this is one has been received? |
|
774
|
|
|
$isReceived = checkPMReceived($pmsg); |
|
775
|
|
|
|
|
776
|
|
|
// Get the quoted message (and make sure you're allowed to see this quote!). |
|
777
|
|
|
$row_quoted = loadPMQuote($pmsg, $isReceived); |
|
778
|
|
|
if ($row_quoted === false) |
|
|
|
|
|
|
779
|
|
|
{ |
|
780
|
|
|
throw new Exception('pm_not_yours', false); |
|
781
|
|
|
} |
|
782
|
|
|
|
|
783
|
|
|
// Censor the message. |
|
784
|
|
|
$row_quoted['subject'] = censor($row_quoted['subject']); |
|
785
|
|
|
$row_quoted['body'] = censor($row_quoted['body']); |
|
786
|
|
|
|
|
787
|
|
|
// Let's make sure we mark this one as read |
|
788
|
|
|
markMessages($pmsg); |
|
789
|
|
|
|
|
790
|
|
|
// Figure out which flavor or 'Re: ' to use |
|
791
|
|
|
$context['response_prefix'] = response_prefix(); |
|
792
|
|
|
|
|
793
|
|
|
$form_subject = $row_quoted['subject']; |
|
794
|
|
|
|
|
795
|
|
|
// Add 'Re: ' to it.... |
|
796
|
|
|
if ($context['reply'] && trim($context['response_prefix']) !== '' && Util::strpos($form_subject, trim($context['response_prefix'])) !== 0) |
|
797
|
|
|
{ |
|
798
|
|
|
$form_subject = $context['response_prefix'] . $form_subject; |
|
799
|
|
|
} |
|
800
|
|
|
|
|
801
|
|
|
// If quoting, let's clean up some things and set the quote header for the pm body |
|
802
|
|
|
if ($this->_req->hasQuery('quote')) |
|
803
|
|
|
{ |
|
804
|
|
|
// Remove any nested quotes and <br />... |
|
805
|
|
|
$form_message = preg_replace('~<br ?/?>~i', "\n", $row_quoted['body']); |
|
806
|
|
|
$form_message = removeNestedQuotes($form_message); |
|
807
|
|
|
|
|
808
|
|
|
if (empty($row_quoted['id_member'])) |
|
809
|
|
|
{ |
|
810
|
|
|
$form_message = '[quote author="' . $row_quoted['real_name'] . '"]' . "\n" . $form_message . "\n" . '[/quote]'; |
|
811
|
|
|
} |
|
812
|
|
|
else |
|
813
|
|
|
{ |
|
814
|
|
|
$form_message = '[quote author=' . $row_quoted['real_name'] . ' link=action=profile;u=' . $row_quoted['id_member'] . ' date=' . $row_quoted['msgtime'] . ']' . "\n" . $form_message . "\n" . '[/quote]'; |
|
815
|
|
|
} |
|
816
|
|
|
} |
|
817
|
|
|
else |
|
818
|
|
|
{ |
|
819
|
|
|
$form_message = ''; |
|
820
|
|
|
} |
|
821
|
|
|
|
|
822
|
|
|
// Allow them to QQ the message they are replying to |
|
823
|
|
|
loadJavascriptFile('quickQuote.js', ['defer' => true]); |
|
824
|
|
|
theme()->addInlineJavascript(" |
|
825
|
|
|
document.addEventListener('DOMContentLoaded', () => new Elk_QuickQuote(), false);", true |
|
826
|
|
|
); |
|
827
|
|
|
|
|
828
|
|
|
// Do the BBC thang on the message. |
|
829
|
|
|
$bbc_parser = ParserWrapper::instance(); |
|
830
|
|
|
$row_quoted['body'] = $bbc_parser->parsePM($row_quoted['body']); |
|
831
|
|
|
|
|
832
|
|
|
// Set up the quoted message array. |
|
833
|
|
|
$context['quoted_message'] = [ |
|
834
|
|
|
'id' => $row_quoted['id_pm'], |
|
835
|
|
|
'pm_head' => $row_quoted['pm_head'], |
|
836
|
|
|
'member' => [ |
|
837
|
|
|
'name' => $row_quoted['real_name'], |
|
838
|
|
|
'username' => $row_quoted['member_name'], |
|
839
|
|
|
'id' => $row_quoted['id_member'], |
|
840
|
|
|
'href' => empty($row_quoted['id_member']) ? '' : getUrl('profile', ['action' => 'profile', 'u' => $row_quoted['id_member']]), |
|
841
|
|
|
'link' => empty($row_quoted['id_member']) ? $row_quoted['real_name'] : '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => $row_quoted['id_member']]) . '">' . $row_quoted['real_name'] . '</a>', |
|
842
|
|
|
], |
|
843
|
|
|
'subject' => $row_quoted['subject'], |
|
844
|
|
|
'time' => standardTime($row_quoted['msgtime']), |
|
845
|
|
|
'html_time' => htmlTime($row_quoted['msgtime']), |
|
846
|
|
|
'timestamp' => forum_time(true, $row_quoted['msgtime']), |
|
847
|
|
|
'body' => $row_quoted['body'] |
|
848
|
|
|
]; |
|
849
|
|
|
} |
|
850
|
|
|
// A new message it is then |
|
851
|
|
|
else |
|
852
|
|
|
{ |
|
853
|
|
|
$context['quoted_message'] = false; |
|
854
|
|
|
$form_subject = ''; |
|
855
|
|
|
$form_message = ''; |
|
856
|
|
|
} |
|
857
|
|
|
|
|
858
|
|
|
// Start of like we don't know where this is going |
|
859
|
|
|
$context['recipients'] = [ |
|
860
|
|
|
'to' => [], |
|
861
|
|
|
'bcc' => [], |
|
862
|
|
|
]; |
|
863
|
|
|
|
|
864
|
|
|
// Sending by ID? Replying to all? Fetch the real_name(s). |
|
865
|
|
|
if ($this->_req->hasQuery('u')) |
|
866
|
|
|
{ |
|
867
|
|
|
// If the user is replying to all, get all the other members this was sent to. |
|
868
|
|
|
$u_param = $this->_req->getQuery('u', 'trim|strval', ''); |
|
869
|
|
|
if ($u_param === 'all' && isset($row_quoted)) |
|
870
|
|
|
{ |
|
871
|
|
|
// Firstly, to reply to all, we clearly already have $row_quoted - so have the original member from. |
|
872
|
|
|
if ($row_quoted['id_member'] != $this->user->id) |
|
873
|
|
|
{ |
|
874
|
|
|
$context['recipients']['to'][] = [ |
|
875
|
|
|
'id' => $row_quoted['id_member'], |
|
876
|
|
|
'name' => htmlspecialchars($row_quoted['real_name'], ENT_COMPAT), |
|
877
|
|
|
]; |
|
878
|
|
|
} |
|
879
|
|
|
|
|
880
|
|
|
// Now to get all the others. |
|
881
|
|
|
$context['recipients']['to'] = array_merge($context['recipients']['to'], isset($pmsg) ? loadPMRecipientsAll($pmsg) : []); |
|
882
|
|
|
} |
|
883
|
|
|
else |
|
884
|
|
|
{ |
|
885
|
|
|
$users_csv = $u_param; |
|
886
|
|
|
$users = $users_csv === '' ? [] : array_map('intval', explode(',', $users_csv)); |
|
887
|
|
|
$users = array_unique($users); |
|
888
|
|
|
|
|
889
|
|
|
// For all the member's this is going to get their display name. |
|
890
|
|
|
require_once(SUBSDIR . '/Members.subs.php'); |
|
891
|
|
|
$result = getBasicMemberData($users); |
|
892
|
|
|
|
|
893
|
|
|
foreach ($result as $row) |
|
894
|
|
|
{ |
|
895
|
|
|
$context['recipients']['to'][] = [ |
|
896
|
|
|
'id' => $row['id_member'], |
|
897
|
|
|
'name' => $row['real_name'], |
|
898
|
|
|
]; |
|
899
|
|
|
} |
|
900
|
|
|
} |
|
901
|
|
|
|
|
902
|
|
|
// Get a literal name list in case the user has JavaScript disabled. |
|
903
|
|
|
$names = []; |
|
904
|
|
|
foreach ($context['recipients']['to'] as $to) |
|
905
|
|
|
{ |
|
906
|
|
|
$names[] = $to['name']; |
|
907
|
|
|
} |
|
908
|
|
|
|
|
909
|
|
|
$context['to_value'] = empty($names) ? '' : '"' . implode('", "', $names) . '"'; |
|
910
|
|
|
} |
|
911
|
|
|
else |
|
912
|
|
|
{ |
|
913
|
|
|
$context['to_value'] = ''; |
|
914
|
|
|
} |
|
915
|
|
|
|
|
916
|
|
|
// Set the defaults... |
|
917
|
|
|
$context['subject'] = $form_subject; |
|
918
|
|
|
$context['message'] = str_replace(['"', '<', '>', ' '], ['"', '<', '>', ' '], $form_message); |
|
919
|
|
|
|
|
920
|
|
|
// And build the link tree. |
|
921
|
|
|
$context['breadcrumbs'][] = [ |
|
922
|
|
|
'url' => getUrl('action', ['action' => 'pm', 'sa' => 'send']), |
|
923
|
|
|
'name' => $txt['new_message'] |
|
924
|
|
|
]; |
|
925
|
|
|
|
|
926
|
|
|
// Needed for the editor. |
|
927
|
|
|
require_once(SUBSDIR . '/Editor.subs.php'); |
|
928
|
|
|
|
|
929
|
|
|
// Now create the editor. |
|
930
|
|
|
$editorOptions = [ |
|
931
|
|
|
'id' => 'message', |
|
932
|
|
|
'value' => $context['message'], |
|
933
|
|
|
'height' => '250px', |
|
934
|
|
|
'width' => '100%', |
|
935
|
|
|
'labels' => [ |
|
936
|
|
|
'post_button' => $txt['send_message'], |
|
937
|
|
|
], |
|
938
|
|
|
'smiley_container' => 'smileyBox_message', |
|
939
|
|
|
'bbc_container' => 'bbcBox_message', |
|
940
|
|
|
'preview_type' => 2, |
|
941
|
|
|
]; |
|
942
|
|
|
|
|
943
|
|
|
// Trigger the prepare_send_context PM event |
|
944
|
|
|
$this->_events->trigger('prepare_send_context', ['editorOptions' => &$editorOptions]); |
|
945
|
|
|
|
|
946
|
|
|
create_control_richedit($editorOptions); |
|
947
|
|
|
|
|
948
|
|
|
// No one is bcc'ed just yet |
|
949
|
|
|
$context['bcc_value'] = ''; |
|
950
|
|
|
|
|
951
|
|
|
// Register this form and get a sequence number in $context. |
|
952
|
|
|
checkSubmitOnce('register'); |
|
953
|
|
|
} |
|
954
|
|
|
|
|
955
|
|
|
/** |
|
956
|
|
|
* An error in the message... |
|
957
|
|
|
* |
|
958
|
|
|
* @param array $named_recipients |
|
959
|
|
|
* @param array $recipient_ids array keys of [bbc] => int[] and [to] => int[] |
|
960
|
|
|
* @param object $msg_options body, subject, and reply values |
|
961
|
|
|
* |
|
962
|
|
|
* @throws Exception pm_not_yours |
|
963
|
|
|
*/ |
|
964
|
|
|
public function messagePostError($named_recipients, $recipient_ids = [], $msg_options = null): void |
|
965
|
|
|
{ |
|
966
|
|
|
global $txt, $context, $modSettings; |
|
967
|
|
|
|
|
968
|
|
|
if ($this->getApi() !== false) |
|
969
|
|
|
{ |
|
970
|
|
|
$context['sub_template'] = 'generic_preview'; |
|
971
|
|
|
} |
|
972
|
|
|
else |
|
973
|
|
|
{ |
|
974
|
|
|
$context['sub_template'] = 'send'; |
|
975
|
|
|
$context['menu_data_' . $context['pm_menu_id']]['current_area'] = 'send'; |
|
976
|
|
|
} |
|
977
|
|
|
|
|
978
|
|
|
$context['page_title'] = $txt['send_message']; |
|
979
|
|
|
$error_types = ErrorContext::context('pm', 1); |
|
980
|
|
|
|
|
981
|
|
|
// Got some known members? |
|
982
|
|
|
$context['recipients'] = [ |
|
983
|
|
|
'to' => [], |
|
984
|
|
|
'bcc' => [], |
|
985
|
|
|
]; |
|
986
|
|
|
|
|
987
|
|
|
if (!empty($recipient_ids['to']) || !empty($recipient_ids['bcc'])) |
|
988
|
|
|
{ |
|
989
|
|
|
$allRecipients = array_merge($recipient_ids['to'], $recipient_ids['bcc']); |
|
990
|
|
|
|
|
991
|
|
|
require_once(SUBSDIR . '/Members.subs.php'); |
|
992
|
|
|
|
|
993
|
|
|
// Get the latest activated member's display name. |
|
994
|
|
|
$result = getBasicMemberData($allRecipients); |
|
995
|
|
|
foreach ($result as $row) |
|
996
|
|
|
{ |
|
997
|
|
|
$recipientType = in_array($row['id_member'], $recipient_ids['bcc']) ? 'bcc' : 'to'; |
|
998
|
|
|
$context['recipients'][$recipientType][] = [ |
|
999
|
|
|
'id' => $row['id_member'], |
|
1000
|
|
|
'name' => $row['real_name'], |
|
1001
|
|
|
]; |
|
1002
|
|
|
} |
|
1003
|
|
|
} |
|
1004
|
|
|
|
|
1005
|
|
|
// Set everything up like before... |
|
1006
|
|
|
if (!empty($msg_options)) |
|
1007
|
|
|
{ |
|
1008
|
|
|
$context['subject'] = $msg_options->subject; |
|
1009
|
|
|
$context['message'] = $msg_options->body; |
|
1010
|
|
|
$context['reply'] = $msg_options->reply_to; |
|
1011
|
|
|
} |
|
1012
|
|
|
else |
|
1013
|
|
|
{ |
|
1014
|
|
|
$subject_in = $this->_req->getPost('subject', 'trim|Util::htmlspecialchars', ''); |
|
1015
|
|
|
$message_in = $this->_req->getPost('message', 'trim|strval|cleanhtml', ''); |
|
1016
|
|
|
$context['subject'] = $subject_in; |
|
1017
|
|
|
$context['message'] = str_replace([' '], [' '], $message_in); |
|
1018
|
|
|
$context['reply'] = $this->_req->getPost('replied_to', 'intval', 0) > 0; |
|
1019
|
|
|
} |
|
1020
|
|
|
|
|
1021
|
|
|
// If this is a reply to a message, we need to reload the quote |
|
1022
|
|
|
if ($context['reply']) |
|
1023
|
|
|
{ |
|
1024
|
|
|
$pmsg = $this->_req->getPost('replied_to', 'intval', 0); |
|
1025
|
|
|
$isReceived = $context['folder'] !== 'sent'; |
|
1026
|
|
|
$row_quoted = loadPMQuote($pmsg, $isReceived); |
|
1027
|
|
|
if ($row_quoted === false) |
|
|
|
|
|
|
1028
|
|
|
{ |
|
1029
|
|
|
if ($this->getApi() === false) |
|
1030
|
|
|
{ |
|
1031
|
|
|
throw new Exception('pm_not_yours', false); |
|
1032
|
|
|
} |
|
1033
|
|
|
|
|
1034
|
|
|
$error_types->addError('pm_not_yours'); |
|
1035
|
|
|
} |
|
1036
|
|
|
else |
|
1037
|
|
|
{ |
|
1038
|
|
|
$row_quoted['subject'] = censor($row_quoted['subject']); |
|
1039
|
|
|
$row_quoted['body'] = censor($row_quoted['body']); |
|
1040
|
|
|
$bbc_parser = ParserWrapper::instance(); |
|
1041
|
|
|
|
|
1042
|
|
|
$context['quoted_message'] = [ |
|
1043
|
|
|
'id' => $row_quoted['id_pm'], |
|
1044
|
|
|
'pm_head' => $row_quoted['pm_head'], |
|
1045
|
|
|
'member' => [ |
|
1046
|
|
|
'name' => $row_quoted['real_name'], |
|
1047
|
|
|
'username' => $row_quoted['member_name'], |
|
1048
|
|
|
'id' => $row_quoted['id_member'], |
|
1049
|
|
|
'href' => empty($row_quoted['id_member']) ? '' : getUrl('profile', ['action' => 'profile', 'u' => $row_quoted['id_member']]), |
|
1050
|
|
|
'link' => empty($row_quoted['id_member']) ? $row_quoted['real_name'] : '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => $row_quoted['id_member']]) . '">' . $row_quoted['real_name'] . '</a>', |
|
1051
|
|
|
], |
|
1052
|
|
|
'subject' => $row_quoted['subject'], |
|
1053
|
|
|
'time' => standardTime($row_quoted['msgtime']), |
|
1054
|
|
|
'html_time' => htmlTime($row_quoted['msgtime']), |
|
1055
|
|
|
'timestamp' => forum_time(true, $row_quoted['msgtime']), |
|
1056
|
|
|
'body' => $bbc_parser->parsePM($row_quoted['body']), |
|
1057
|
|
|
]; |
|
1058
|
|
|
} |
|
1059
|
|
|
} |
|
1060
|
|
|
|
|
1061
|
|
|
// Build the link tree... |
|
1062
|
|
|
$context['breadcrumbs'][] = [ |
|
1063
|
|
|
'url' => getUrl('action', ['action' => 'pm', 'sa' => 'send']), |
|
1064
|
|
|
'name' => $txt['new_message'] |
|
1065
|
|
|
]; |
|
1066
|
|
|
|
|
1067
|
|
|
// Set each of the errors for the template. |
|
1068
|
|
|
$context['post_error'] = [ |
|
1069
|
|
|
'errors' => $error_types->prepareErrors(), |
|
1070
|
|
|
'type' => $error_types->getErrorType() == 0 ? 'minor' : 'serious', |
|
1071
|
|
|
'title' => $txt['error_while_submitting'], |
|
1072
|
|
|
]; |
|
1073
|
|
|
|
|
1074
|
|
|
// We need to load the editor once more. |
|
1075
|
|
|
require_once(SUBSDIR . '/Editor.subs.php'); |
|
1076
|
|
|
|
|
1077
|
|
|
// Create it... |
|
1078
|
|
|
$editorOptions = [ |
|
1079
|
|
|
'id' => 'message', |
|
1080
|
|
|
'value' => $context['message'], |
|
1081
|
|
|
'width' => '100%', |
|
1082
|
|
|
'height' => '250px', |
|
1083
|
|
|
'labels' => [ |
|
1084
|
|
|
'post_button' => $txt['send_message'], |
|
1085
|
|
|
], |
|
1086
|
|
|
'smiley_container' => 'smileyBox_message', |
|
1087
|
|
|
'bbc_container' => 'bbcBox_message', |
|
1088
|
|
|
'preview_type' => 2, |
|
1089
|
|
|
]; |
|
1090
|
|
|
|
|
1091
|
|
|
// Trigger the prepare_send_context PM event |
|
1092
|
|
|
$this->_events->trigger('prepare_send_context', ['editorOptions' => &$editorOptions]); |
|
1093
|
|
|
|
|
1094
|
|
|
create_control_richedit($editorOptions); |
|
1095
|
|
|
|
|
1096
|
|
|
// Check whether we need to show the code again. |
|
1097
|
|
|
$context['require_verification'] = $this->user->is_admin === false && !empty($modSettings['pm_posts_verification']) && $this->user->posts < $modSettings['pm_posts_verification']; |
|
|
|
|
|
|
1098
|
|
|
if ($context['require_verification'] && $this->getApi() === false) |
|
1099
|
|
|
{ |
|
1100
|
|
|
$verificationOptions = [ |
|
1101
|
|
|
'id' => 'pm', |
|
1102
|
|
|
]; |
|
1103
|
|
|
$context['require_verification'] = VerificationControlsIntegrate::create($verificationOptions); |
|
1104
|
|
|
$context['visual_verification_id'] = $verificationOptions['id']; |
|
1105
|
|
|
} |
|
1106
|
|
|
|
|
1107
|
|
|
$context['to_value'] = empty($named_recipients['to']) ? '' : '"' . implode('", "', $named_recipients['to']) . '"'; |
|
1108
|
|
|
$context['bcc_value'] = empty($named_recipients['bcc']) ? '' : '"' . implode('", "', $named_recipients['bcc']) . '"'; |
|
1109
|
|
|
|
|
1110
|
|
|
// No check for the previous submission is needed. |
|
1111
|
|
|
checkSubmitOnce('free'); |
|
1112
|
|
|
|
|
1113
|
|
|
// Acquire a new form sequence number. |
|
1114
|
|
|
checkSubmitOnce('register'); |
|
1115
|
|
|
} |
|
1116
|
|
|
|
|
1117
|
|
|
/** |
|
1118
|
|
|
* Send a personal message. |
|
1119
|
|
|
*/ |
|
1120
|
|
|
public function action_send2() |
|
1121
|
|
|
{ |
|
1122
|
|
|
global $txt, $context, $modSettings; |
|
1123
|
|
|
|
|
1124
|
|
|
// All the helpers we need |
|
1125
|
|
|
require_once(SUBSDIR . '/Auth.subs.php'); |
|
1126
|
|
|
require_once(SUBSDIR . '/Post.subs.php'); |
|
1127
|
|
|
|
|
1128
|
|
|
Txt::load('PersonalMessage', false); |
|
1129
|
|
|
|
|
1130
|
|
|
// Extract out the spam settings - it saves database space! |
|
1131
|
|
|
[$modSettings['max_pm_recipients'], $modSettings['pm_posts_verification'], $modSettings['pm_posts_per_hour']] = explode(',', $modSettings['pm_spam_settings']); |
|
1132
|
|
|
|
|
1133
|
|
|
// Initialize the errors we're about to make. |
|
1134
|
|
|
$post_errors = ErrorContext::context('pm', 1); |
|
1135
|
|
|
|
|
1136
|
|
|
// Check whether we've gone over the limit of messages we can send per hour - fatal error if fails! |
|
1137
|
|
|
if (!empty($modSettings['pm_posts_per_hour']) |
|
1138
|
|
|
&& !allowedTo(['admin_forum', 'moderate_forum', 'send_mail']) |
|
1139
|
|
|
&& $this->user->mod_cache['bq'] === '0=1' |
|
|
|
|
|
|
1140
|
|
|
&& $this->user->mod_cache['gq'] === '0=1') |
|
1141
|
|
|
{ |
|
1142
|
|
|
// How many they sent this last hour? |
|
1143
|
|
|
$pmCount = pmCount($this->user->id, 3600); |
|
|
|
|
|
|
1144
|
|
|
|
|
1145
|
|
|
if (!empty($pmCount) && $pmCount >= $modSettings['pm_posts_per_hour']) |
|
1146
|
|
|
{ |
|
1147
|
|
|
if ($this->getApi() === false) |
|
1148
|
|
|
{ |
|
1149
|
|
|
throw new Exception('pm_too_many_per_hour', true, [$modSettings['pm_posts_per_hour']]); |
|
1150
|
|
|
} |
|
1151
|
|
|
|
|
1152
|
|
|
$post_errors->addError('pm_too_many_per_hour'); |
|
1153
|
|
|
} |
|
1154
|
|
|
} |
|
1155
|
|
|
|
|
1156
|
|
|
// If your session timed out, show an error, but do allow to re-submit. |
|
1157
|
|
|
if ($this->getApi() === false && checkSession('post', '', false) !== '') |
|
1158
|
|
|
{ |
|
1159
|
|
|
$post_errors->addError('session_timeout'); |
|
1160
|
|
|
} |
|
1161
|
|
|
|
|
1162
|
|
|
// Local sanitized copies used for preview/sending |
|
1163
|
|
|
$subject = $this->_req->getPost('subject', 'trim|strval', ''); |
|
1164
|
|
|
$subject = strtr(Util::htmltrim($subject), ["\r" => '', "\n" => '', "\t" => '']); |
|
1165
|
|
|
$message = $this->_req->getPost('message', 'trim|strval', ''); |
|
1166
|
|
|
|
|
1167
|
|
|
$this->_req->post->to = $this->_req->getPost('to', 'trim', empty($this->_req->query->to) ? '' : $this->_req->query->to); |
|
1168
|
|
|
$this->_req->post->bcc = $this->_req->getPost('bcc', 'trim', empty($this->_req->query->bcc) ? '' : $this->_req->query->bcc); |
|
1169
|
|
|
|
|
1170
|
|
|
// Route the input from the 'u' parameter to the 'to'-list. |
|
1171
|
|
|
if (!empty($this->_req->post->u)) |
|
1172
|
|
|
{ |
|
1173
|
|
|
$this->_req->post->recipient_to = explode(',', $this->_req->post->u); |
|
1174
|
|
|
} |
|
1175
|
|
|
|
|
1176
|
|
|
$bbc_parser = ParserWrapper::instance(); |
|
1177
|
|
|
|
|
1178
|
|
|
// Construct the list of recipients. |
|
1179
|
|
|
$recipientList = []; |
|
1180
|
|
|
$namedRecipientList = []; |
|
1181
|
|
|
$namesNotFound = []; |
|
1182
|
|
|
foreach (['to', 'bcc'] as $recipientType) |
|
1183
|
|
|
{ |
|
1184
|
|
|
// First, let's see if there's user ID's given. |
|
1185
|
|
|
$recipientList[$recipientType] = []; |
|
1186
|
|
|
$type = 'recipient_' . $recipientType; |
|
1187
|
|
|
if (!empty($this->_req->post->{$type}) && is_array($this->_req->post->{$type})) |
|
1188
|
|
|
{ |
|
1189
|
|
|
$recipientList[$recipientType] = array_map('intval', $this->_req->post->{$type}); |
|
1190
|
|
|
} |
|
1191
|
|
|
|
|
1192
|
|
|
// Are there also literal names set? |
|
1193
|
|
|
if (!empty($this->_req->post->{$recipientType})) |
|
1194
|
|
|
{ |
|
1195
|
|
|
// We're going to take out the "s anyway ;). |
|
1196
|
|
|
$recipientString = strtr($this->_req->post->{$recipientType}, ['\\"' => '"']); |
|
1197
|
|
|
|
|
1198
|
|
|
preg_match_all('~"([^"]+)"~', $recipientString, $matches); |
|
1199
|
|
|
$namedRecipientList[$recipientType] = array_unique(array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $recipientString)))); |
|
1200
|
|
|
|
|
1201
|
|
|
// Clean any literal names entered |
|
1202
|
|
|
foreach ($namedRecipientList[$recipientType] as $index => $recipient) |
|
1203
|
|
|
{ |
|
1204
|
|
|
if (trim($recipient) !== '') |
|
1205
|
|
|
{ |
|
1206
|
|
|
$namedRecipientList[$recipientType][$index] = Util::htmlspecialchars(Util::strtolower(trim($recipient))); |
|
1207
|
|
|
} |
|
1208
|
|
|
else |
|
1209
|
|
|
{ |
|
1210
|
|
|
unset($namedRecipientList[$recipientType][$index]); |
|
1211
|
|
|
} |
|
1212
|
|
|
} |
|
1213
|
|
|
|
|
1214
|
|
|
// Now see if we can resolve any entered name (not suggest selected) to an actual user |
|
1215
|
|
|
if (!empty($namedRecipientList[$recipientType])) |
|
1216
|
|
|
{ |
|
1217
|
|
|
$foundMembers = findMembers($namedRecipientList[$recipientType]); |
|
1218
|
|
|
|
|
1219
|
|
|
// Assume all are not found until proven otherwise. |
|
1220
|
|
|
$namesNotFound[$recipientType] = $namedRecipientList[$recipientType]; |
|
1221
|
|
|
|
|
1222
|
|
|
// Make sure we only have each member listed once, in case they did not use the select list |
|
1223
|
|
|
foreach ($foundMembers as $member) |
|
1224
|
|
|
{ |
|
1225
|
|
|
$testNames = [ |
|
1226
|
|
|
Util::strtolower($member['username']), |
|
1227
|
|
|
Util::strtolower($member['name']), |
|
1228
|
|
|
Util::strtolower($member['email']), |
|
1229
|
|
|
]; |
|
1230
|
|
|
|
|
1231
|
|
|
if (array_intersect($testNames, $namedRecipientList[$recipientType]) !== []) |
|
1232
|
|
|
{ |
|
1233
|
|
|
$recipientList[$recipientType][] = $member['id']; |
|
1234
|
|
|
|
|
1235
|
|
|
// Get rid of this username, since we found it. |
|
1236
|
|
|
$namesNotFound[$recipientType] = array_diff($namesNotFound[$recipientType], $testNames); |
|
1237
|
|
|
} |
|
1238
|
|
|
} |
|
1239
|
|
|
} |
|
1240
|
|
|
} |
|
1241
|
|
|
|
|
1242
|
|
|
// Selected a recipient to be deleted? Remove them now. |
|
1243
|
|
|
$delete_recipient = $this->_req->getPost('delete_recipient', 'intval', 0); |
|
1244
|
|
|
if (!empty($delete_recipient)) |
|
1245
|
|
|
{ |
|
1246
|
|
|
$recipientList[$recipientType] = array_diff($recipientList[$recipientType], [$delete_recipient]); |
|
1247
|
|
|
} |
|
1248
|
|
|
|
|
1249
|
|
|
// Make sure we don't include the same name twice |
|
1250
|
|
|
$recipientList[$recipientType] = array_unique($recipientList[$recipientType]); |
|
1251
|
|
|
} |
|
1252
|
|
|
|
|
1253
|
|
|
// Are we changing the recipients somehow? |
|
1254
|
|
|
$is_recipient_change = $this->_req->hasPost('delete_recipient') || $this->_req->hasPost('to_submit') || $this->_req->hasPost('bcc_submit'); |
|
1255
|
|
|
|
|
1256
|
|
|
// Check if there's at least one recipient. |
|
1257
|
|
|
if (empty($recipientList['to']) && empty($recipientList['bcc'])) |
|
1258
|
|
|
{ |
|
1259
|
|
|
$post_errors->addError('no_to'); |
|
1260
|
|
|
} |
|
1261
|
|
|
|
|
1262
|
|
|
// Make sure that we remove the members who did get it from the screen. |
|
1263
|
|
|
if (!$is_recipient_change) |
|
1264
|
|
|
{ |
|
1265
|
|
|
foreach (array_keys($recipientList) as $recipientType) |
|
1266
|
|
|
{ |
|
1267
|
|
|
if (!empty($namesNotFound[$recipientType])) |
|
1268
|
|
|
{ |
|
1269
|
|
|
$post_errors->addError('bad_' . $recipientType); |
|
1270
|
|
|
|
|
1271
|
|
|
// Since we already have a post error, remove the previous one. |
|
1272
|
|
|
$post_errors->removeError('no_to'); |
|
1273
|
|
|
|
|
1274
|
|
|
foreach ($namesNotFound[$recipientType] as $name) |
|
1275
|
|
|
{ |
|
1276
|
|
|
$context['send_log']['failed'][] = sprintf($txt['pm_error_user_not_found'], $name); |
|
1277
|
|
|
} |
|
1278
|
|
|
} |
|
1279
|
|
|
} |
|
1280
|
|
|
} |
|
1281
|
|
|
|
|
1282
|
|
|
// Did they make any mistakes like no subject or message? |
|
1283
|
|
|
if ($subject === '') |
|
1284
|
|
|
{ |
|
1285
|
|
|
$post_errors->addError('no_subject'); |
|
1286
|
|
|
} |
|
1287
|
|
|
|
|
1288
|
|
|
if ($message === '') |
|
1289
|
|
|
{ |
|
1290
|
|
|
$post_errors->addError('no_message'); |
|
1291
|
|
|
} |
|
1292
|
|
|
elseif (!empty($modSettings['max_messageLength']) && Util::strlen($message) > $modSettings['max_messageLength']) |
|
1293
|
|
|
{ |
|
1294
|
|
|
$post_errors->addError('long_message'); |
|
1295
|
|
|
} |
|
1296
|
|
|
else |
|
1297
|
|
|
{ |
|
1298
|
|
|
// Preparse the message. |
|
1299
|
|
|
preparsecode($message); |
|
1300
|
|
|
|
|
1301
|
|
|
// Make sure there's still some content left without the tags. |
|
1302
|
|
|
if (Util::htmltrim(strip_tags($bbc_parser->parsePM(Util::htmlspecialchars($message, ENT_QUOTES)), '<img>')) === '' |
|
1303
|
|
|
&& (!allowedTo('admin_forum') || !str_contains($message, '[html]'))) |
|
1304
|
|
|
{ |
|
1305
|
|
|
$post_errors->addError('no_message'); |
|
1306
|
|
|
} |
|
1307
|
|
|
} |
|
1308
|
|
|
|
|
1309
|
|
|
// If they made any errors, give them a chance to make amends. |
|
1310
|
|
|
if ($post_errors->hasErrors() |
|
1311
|
|
|
&& !$is_recipient_change |
|
1312
|
|
|
&& !$this->_req->isSet('preview') |
|
1313
|
|
|
&& $this->getApi() === false) |
|
1314
|
|
|
{ |
|
1315
|
|
|
$this->messagePostError($namedRecipientList, $recipientList); |
|
1316
|
|
|
|
|
1317
|
|
|
return false; |
|
1318
|
|
|
} |
|
1319
|
|
|
|
|
1320
|
|
|
// Want to take a second glance before you send? |
|
1321
|
|
|
if ($this->_req->isSet('preview')) |
|
1322
|
|
|
{ |
|
1323
|
|
|
// Set everything up to be displayed. |
|
1324
|
|
|
$context['preview_subject'] = Util::htmlspecialchars($this->_req->getPost('subject', 'trim|strval', '')); |
|
1325
|
|
|
$context['preview_message'] = Util::htmlspecialchars($this->_req->getPost('message', 'trim|strval',''),ENT_QUOTES, 'UTF-8', true); |
|
1326
|
|
|
preparsecode($context['preview_message'], true); |
|
1327
|
|
|
|
|
1328
|
|
|
// Parse out the BBC if it is enabled. |
|
1329
|
|
|
$context['preview_message'] = $bbc_parser->parsePM($context['preview_message']); |
|
1330
|
|
|
|
|
1331
|
|
|
// Censor, as always. |
|
1332
|
|
|
$context['preview_subject'] = censor($context['preview_subject']); |
|
1333
|
|
|
$context['preview_message'] = censor($context['preview_message']); |
|
1334
|
|
|
|
|
1335
|
|
|
// Set a descriptive title. |
|
1336
|
|
|
$context['page_title'] = $txt['preview'] . ' - ' . $context['preview_subject']; |
|
1337
|
|
|
|
|
1338
|
|
|
// Pretend they messed up but don't ignore if they really did :P. |
|
1339
|
|
|
$this->messagePostError($namedRecipientList, $recipientList); |
|
1340
|
|
|
|
|
1341
|
|
|
return false; |
|
1342
|
|
|
} |
|
1343
|
|
|
|
|
1344
|
|
|
if ($is_recipient_change) |
|
1345
|
|
|
{ |
|
1346
|
|
|
// Maybe we couldn't find one? |
|
1347
|
|
|
foreach ($namesNotFound as $recipientType => $names) |
|
1348
|
|
|
{ |
|
1349
|
|
|
$post_errors->addError('bad_' . $recipientType); |
|
1350
|
|
|
foreach ($names as $name) |
|
1351
|
|
|
{ |
|
1352
|
|
|
$context['send_log']['failed'][] = sprintf($txt['pm_error_user_not_found'], $name); |
|
1353
|
|
|
} |
|
1354
|
|
|
} |
|
1355
|
|
|
|
|
1356
|
|
|
$this->messagePostError($namedRecipientList, $recipientList); |
|
1357
|
|
|
|
|
1358
|
|
|
return true; |
|
1359
|
|
|
} |
|
1360
|
|
|
|
|
1361
|
|
|
// Adding a recipient cause JavaScript ain't working? |
|
1362
|
|
|
try |
|
1363
|
|
|
{ |
|
1364
|
|
|
$this->_events->trigger('before_sending', ['namedRecipientList' => $namedRecipientList, 'recipientList' => $recipientList, 'namesNotFound' => $namesNotFound, 'post_errors' => $post_errors]); |
|
1365
|
|
|
} |
|
1366
|
|
|
catch (ControllerRedirectException) |
|
1367
|
|
|
{ |
|
1368
|
|
|
$this->messagePostError($namedRecipientList, $recipientList); |
|
1369
|
|
|
|
|
1370
|
|
|
return true; |
|
1371
|
|
|
} |
|
1372
|
|
|
|
|
1373
|
|
|
// Safety net, it may be a module may just add to the list of errors without actually throw the error |
|
1374
|
|
|
if ($post_errors->hasErrors() && !$this->_req->isSet('preview') && $this->getApi() === false) |
|
1375
|
|
|
{ |
|
1376
|
|
|
$this->messagePostError($namedRecipientList, $recipientList); |
|
1377
|
|
|
|
|
1378
|
|
|
return false; |
|
1379
|
|
|
} |
|
1380
|
|
|
|
|
1381
|
|
|
// Before we send the PM, let's make sure we don't have an abuse of numbers. |
|
1382
|
|
|
if (!empty($modSettings['max_pm_recipients']) && count($recipientList['to']) + count($recipientList['bcc']) > $modSettings['max_pm_recipients'] && !allowedTo(['moderate_forum', 'send_mail', 'admin_forum'])) |
|
1383
|
|
|
{ |
|
1384
|
|
|
$context['send_log'] = [ |
|
1385
|
|
|
'sent' => [], |
|
1386
|
|
|
'failed' => [sprintf($txt['pm_too_many_recipients'], $modSettings['max_pm_recipients'])], |
|
1387
|
|
|
]; |
|
1388
|
|
|
|
|
1389
|
|
|
$this->messagePostError($namedRecipientList, $recipientList); |
|
1390
|
|
|
|
|
1391
|
|
|
return false; |
|
1392
|
|
|
} |
|
1393
|
|
|
|
|
1394
|
|
|
// Protect from message spamming. |
|
1395
|
|
|
spamProtection('pm'); |
|
1396
|
|
|
|
|
1397
|
|
|
// Prevent double submission of this form. |
|
1398
|
|
|
checkSubmitOnce('check'); |
|
1399
|
|
|
|
|
1400
|
|
|
// Finally, do the actual sending of the PM. |
|
1401
|
|
|
if (!empty($recipientList['to']) || !empty($recipientList['bcc'])) |
|
1402
|
|
|
{ |
|
1403
|
|
|
// Reset the message to pre-check condition, sendpm will do the rest. |
|
1404
|
|
|
$subject = $this->_req->getPost('subject', 'trim|strval', ''); |
|
1405
|
|
|
$message = $this->_req->getPost('message', 'trim|strval', ''); |
|
1406
|
|
|
$context['send_log'] = sendpm($recipientList, $subject, $message, true, null, empty($this->_req->post->pm_head) ? 0 : (int) $this->_req->post->pm_head); |
|
1407
|
|
|
} |
|
1408
|
|
|
else |
|
1409
|
|
|
{ |
|
1410
|
|
|
$context['send_log'] = [ |
|
1411
|
|
|
'sent' => [], |
|
1412
|
|
|
'failed' => [] |
|
1413
|
|
|
]; |
|
1414
|
|
|
} |
|
1415
|
|
|
|
|
1416
|
|
|
// Mark the message as "replied to". |
|
1417
|
|
|
$replied_to = $this->_req->getPost('replied_to', 'intval', 0); |
|
1418
|
|
|
$box = $this->_req->getPost('f', 'trim', ''); |
|
1419
|
|
|
if (!empty($context['send_log']['sent']) && !empty($replied_to) && $box === 'inbox') |
|
1420
|
|
|
{ |
|
1421
|
|
|
require_once(SUBSDIR . '/PersonalMessage.subs.php'); |
|
1422
|
|
|
setPMRepliedStatus($this->user->id, $replied_to); |
|
1423
|
|
|
} |
|
1424
|
|
|
|
|
1425
|
|
|
$failed = !empty($context['send_log']['failed']); |
|
1426
|
|
|
$this->_events->trigger('message_sent', ['failed' => $failed]); |
|
1427
|
|
|
|
|
1428
|
|
|
// If one or more of the recipients were invalid, go back to the post screen with the failed usernames. |
|
1429
|
|
|
if ($failed) |
|
1430
|
|
|
{ |
|
1431
|
|
|
$this->messagePostError($namesNotFound, [ |
|
1432
|
|
|
'to' => array_intersect($recipientList['to'], $context['send_log']['failed']), |
|
1433
|
|
|
'bcc' => array_intersect($recipientList['bcc'], $context['send_log']['failed']) |
|
1434
|
|
|
]); |
|
1435
|
|
|
|
|
1436
|
|
|
return false; |
|
1437
|
|
|
} |
|
1438
|
|
|
|
|
1439
|
|
|
// Message sent successfully |
|
1440
|
|
|
$context['current_label_redirect'] .= ';done=sent'; |
|
1441
|
|
|
|
|
1442
|
|
|
// Go back to where they sent from, if possible... |
|
1443
|
|
|
redirectexit($context['current_label_redirect']); |
|
1444
|
|
|
} |
|
1445
|
|
|
|
|
1446
|
|
|
/** |
|
1447
|
|
|
* This function performs all additional actions including the deleting |
|
1448
|
|
|
* and labeling of PM's |
|
1449
|
|
|
*/ |
|
1450
|
|
|
public function action_pmactions(): void |
|
1451
|
|
|
{ |
|
1452
|
|
|
global $context; |
|
1453
|
|
|
|
|
1454
|
|
|
checkSession('request'); |
|
1455
|
|
|
|
|
1456
|
|
|
// Sending in the single pm choice via GET |
|
1457
|
|
|
$pm_actions = $this->_req->getQuery('pm_actions', null, ''); |
|
1458
|
|
|
|
|
1459
|
|
|
// Set the action to apply to the PMs defined by pm_actions (yes, it is that brilliant) |
|
1460
|
|
|
$pm_action = $this->_req->getPost('pm_action', 'trim', ''); |
|
1461
|
|
|
$pm_action = empty($pm_action) && $this->_req->hasPost('del_selected') ? 'delete' : $pm_action; |
|
1462
|
|
|
|
|
1463
|
|
|
// Create a list of PMs that we need to work on |
|
1464
|
|
|
$pms_list = $this->_req->getPost('pms', null, []); |
|
1465
|
|
|
if ($pm_action !== '' |
|
1466
|
|
|
&& !empty($pms_list) |
|
1467
|
|
|
&& is_array($pms_list)) |
|
1468
|
|
|
{ |
|
1469
|
|
|
$pm_actions = []; |
|
1470
|
|
|
foreach ($pms_list as $pm) |
|
1471
|
|
|
{ |
|
1472
|
|
|
$pm_actions[(int) $pm] = $pm_action; |
|
1473
|
|
|
} |
|
1474
|
|
|
} |
|
1475
|
|
|
|
|
1476
|
|
|
// No messages to action then bug out |
|
1477
|
|
|
if (empty($pm_actions)) |
|
1478
|
|
|
{ |
|
1479
|
|
|
redirectexit($context['current_label_redirect']); |
|
1480
|
|
|
} |
|
1481
|
|
|
|
|
1482
|
|
|
// If we are in conversation, we may need to apply this to every message in that conversation. |
|
1483
|
|
|
if ($context['display_mode'] === PmHelper::DISPLAY_AS_CONVERSATION && $this->_req->hasQuery('conversation')) |
|
1484
|
|
|
{ |
|
1485
|
|
|
$id_pms = array_map('intval', array_keys($pm_actions)); |
|
1486
|
|
|
$pm_heads = getDiscussions($id_pms); |
|
1487
|
|
|
$pms = getPmsFromDiscussion(array_keys($pm_heads)); |
|
1488
|
|
|
|
|
1489
|
|
|
// Copy the action from the single to PM to the others in the conversation. |
|
1490
|
|
|
foreach ($pms as $id_pm => $id_head) |
|
1491
|
|
|
{ |
|
1492
|
|
|
if (isset($pm_heads[$id_head], $pm_actions[$pm_heads[$id_head]])) |
|
1493
|
|
|
{ |
|
1494
|
|
|
$pm_actions[$id_pm] = $pm_actions[$pm_heads[$id_head]]; |
|
1495
|
|
|
} |
|
1496
|
|
|
} |
|
1497
|
|
|
} |
|
1498
|
|
|
|
|
1499
|
|
|
// Get to doing what we've been told |
|
1500
|
|
|
$to_delete = []; |
|
1501
|
|
|
$to_label = []; |
|
1502
|
|
|
$label_type = []; |
|
1503
|
|
|
foreach ($pm_actions as $pm => $action) |
|
1504
|
|
|
{ |
|
1505
|
|
|
// What are we doing with the selected messages, adding a label, removing, other? |
|
1506
|
|
|
switch (substr($action, 0, 4)) |
|
1507
|
|
|
{ |
|
1508
|
|
|
case 'dele': |
|
1509
|
|
|
$to_delete[] = (int) $pm; |
|
1510
|
|
|
break; |
|
1511
|
|
|
case 'add_': |
|
1512
|
|
|
$type = 'add'; |
|
1513
|
|
|
$action = substr($action, 4); |
|
1514
|
|
|
break; |
|
1515
|
|
|
case 'rem_': |
|
1516
|
|
|
$type = 'rem'; |
|
1517
|
|
|
$action = substr($action, 4); |
|
1518
|
|
|
break; |
|
1519
|
|
|
default: |
|
1520
|
|
|
$type = 'unk'; |
|
1521
|
|
|
} |
|
1522
|
|
|
|
|
1523
|
|
|
if ((int) $action === -1 || (int) $action === 0 || (int) $action > 0) |
|
1524
|
|
|
{ |
|
1525
|
|
|
$to_label[(int) $pm] = (int) $action; |
|
1526
|
|
|
$label_type[(int) $pm] = $type ?? ''; |
|
1527
|
|
|
} |
|
1528
|
|
|
} |
|
1529
|
|
|
|
|
1530
|
|
|
// Deleting, it looks like? |
|
1531
|
|
|
if (!empty($to_delete)) |
|
1532
|
|
|
{ |
|
1533
|
|
|
deleteMessages($to_delete, $context['display_mode'] === PmHelper::DISPLAY_AS_CONVERSATION ? null : $context['folder']); |
|
1534
|
|
|
} |
|
1535
|
|
|
|
|
1536
|
|
|
// Are we labeling anything? |
|
1537
|
|
|
if (!empty($to_label) && $context['folder'] === 'inbox') |
|
1538
|
|
|
{ |
|
1539
|
|
|
$updateErrors = changePMLabels($to_label, $label_type, $this->user->id); |
|
1540
|
|
|
|
|
1541
|
|
|
// Any errors? |
|
1542
|
|
|
if (!empty($updateErrors)) |
|
1543
|
|
|
{ |
|
1544
|
|
|
throw new Exception('labels_too_many', false, [$updateErrors]); |
|
1545
|
|
|
} |
|
1546
|
|
|
} |
|
1547
|
|
|
|
|
1548
|
|
|
// Back to the folder. |
|
1549
|
|
|
$_SESSION['pm_selected'] = array_keys($to_label); |
|
1550
|
|
|
redirectexit($context['current_label_redirect'] . (count($to_label) === 1 ? '#msg_' . $_SESSION['pm_selected'][0] : '')); |
|
1551
|
|
|
} |
|
1552
|
|
|
|
|
1553
|
|
|
/** |
|
1554
|
|
|
* Are you sure you want to PERMANENTLY (mostly) delete ALL your messages? |
|
1555
|
|
|
*/ |
|
1556
|
|
|
public function action_removeall(): void |
|
1557
|
|
|
{ |
|
1558
|
|
|
global $txt, $context; |
|
1559
|
|
|
|
|
1560
|
|
|
// Only have to set up the template... |
|
1561
|
|
|
$context['sub_template'] = 'ask_delete'; |
|
1562
|
|
|
$context['page_title'] = $txt['delete_all']; |
|
1563
|
|
|
$folder_flag = $this->_req->getQuery('f', 'trim|strval', ''); |
|
1564
|
|
|
$context['delete_all'] = $folder_flag === 'all'; |
|
1565
|
|
|
|
|
1566
|
|
|
// And set the folder name... |
|
1567
|
|
|
$txt['delete_all'] = str_replace('PMBOX', $context['folder'] != 'sent' ? $txt['inbox'] : $txt['sent_items'], $txt['delete_all']); |
|
1568
|
|
|
} |
|
1569
|
|
|
|
|
1570
|
|
|
/** |
|
1571
|
|
|
* Delete ALL the messages! |
|
1572
|
|
|
*/ |
|
1573
|
|
|
public function action_removeall2(): void |
|
1574
|
|
|
{ |
|
1575
|
|
|
global $context; |
|
1576
|
|
|
|
|
1577
|
|
|
checkSession('get'); |
|
1578
|
|
|
|
|
1579
|
|
|
// If all, then delete all messages the user has. |
|
1580
|
|
|
$folder_flag = $this->_req->getQuery('f', 'trim|strval', ''); |
|
1581
|
|
|
if ($folder_flag === 'all') |
|
1582
|
|
|
{ |
|
1583
|
|
|
deleteMessages(null); |
|
1584
|
|
|
} |
|
1585
|
|
|
// Otherwise just the selected folder. |
|
1586
|
|
|
else |
|
1587
|
|
|
{ |
|
1588
|
|
|
deleteMessages(null, $folder_flag !== 'sent' ? 'inbox' : 'sent'); |
|
1589
|
|
|
} |
|
1590
|
|
|
|
|
1591
|
|
|
// Done... all gone. |
|
1592
|
|
|
redirectexit($context['current_label_redirect']); |
|
1593
|
|
|
} |
|
1594
|
|
|
|
|
1595
|
|
|
/** |
|
1596
|
|
|
* This function allows the user to prune (delete) all messages older than a supplied duration. |
|
1597
|
|
|
*/ |
|
1598
|
|
|
public function action_prune(): void |
|
1599
|
|
|
{ |
|
1600
|
|
|
global $txt, $context; |
|
1601
|
|
|
|
|
1602
|
|
|
// Actually delete the messages. |
|
1603
|
|
|
if ($this->_req->hasPost('age')) |
|
1604
|
|
|
{ |
|
1605
|
|
|
checkSession(); |
|
1606
|
|
|
|
|
1607
|
|
|
// Calculate the time to delete before. |
|
1608
|
|
|
$age_days = $this->_req->getPost('age', 'intval', 0); |
|
1609
|
|
|
$deleteTime = max(0, time() - (86400 * $age_days)); |
|
1610
|
|
|
|
|
1611
|
|
|
// Select all the messages older than $deleteTime. |
|
1612
|
|
|
$toDelete = getPMsOlderThan($this->user->id, $deleteTime); |
|
1613
|
|
|
|
|
1614
|
|
|
// Delete the actual messages. |
|
1615
|
|
|
deleteMessages($toDelete); |
|
1616
|
|
|
|
|
1617
|
|
|
// Go back to their inbox. |
|
1618
|
|
|
redirectexit($context['current_label_redirect']); |
|
1619
|
|
|
} |
|
1620
|
|
|
|
|
1621
|
|
|
// Build the link tree elements. |
|
1622
|
|
|
$context['breadcrumbs'][] = [ |
|
1623
|
|
|
'url' => getUrl('action', ['action' => 'pm', 'sa' => 'prune']), |
|
1624
|
|
|
'name' => $txt['pm_prune'] |
|
1625
|
|
|
]; |
|
1626
|
|
|
$context['sub_template'] = 'prune'; |
|
1627
|
|
|
$context['page_title'] = $txt['pm_prune']; |
|
1628
|
|
|
} |
|
1629
|
|
|
|
|
1630
|
|
|
/** |
|
1631
|
|
|
* This function handles adding, deleting, and editing labels on messages. |
|
1632
|
|
|
*/ |
|
1633
|
|
|
public function action_manlabels(): void |
|
1634
|
|
|
{ |
|
1635
|
|
|
$controller = new Labels($this->_events); |
|
1636
|
|
|
$controller->setUser($this->user); |
|
1637
|
|
|
$controller->action_manlabels(); |
|
1638
|
|
|
} |
|
1639
|
|
|
|
|
1640
|
|
|
/** |
|
1641
|
|
|
* Allows editing Personal Message Settings. |
|
1642
|
|
|
* |
|
1643
|
|
|
* @uses ProfileOptions controller. (@todo refactor this.) |
|
1644
|
|
|
* @uses Profile template. |
|
1645
|
|
|
* @uses Profile language file. |
|
1646
|
|
|
*/ |
|
1647
|
|
|
public function action_settings(): void |
|
1648
|
|
|
{ |
|
1649
|
|
|
$controller = new Settings($this->_events); |
|
|
|
|
|
|
1650
|
|
|
$controller->setUser($this->user); |
|
1651
|
|
|
$controller->action_settings(); |
|
1652
|
|
|
} |
|
1653
|
|
|
|
|
1654
|
|
|
/** |
|
1655
|
|
|
* Allows the user to report a personal message to an administrator. |
|
1656
|
|
|
* |
|
1657
|
|
|
* What it does: |
|
1658
|
|
|
* |
|
1659
|
|
|
* - In the first instance requires that the ID of the message to report is passed through $_GET. |
|
1660
|
|
|
* - It allows the user to report to either a particular administrator - or the whole admin team. |
|
1661
|
|
|
* - It will forward on a copy of the original message without allowing the reporter to make changes. |
|
1662
|
|
|
* |
|
1663
|
|
|
* @uses report_message sub-template. |
|
1664
|
|
|
*/ |
|
1665
|
|
|
public function action_report(): void |
|
1666
|
|
|
{ |
|
1667
|
|
|
$controller = new Report($this->_events); |
|
1668
|
|
|
$controller->setUser($this->user); |
|
1669
|
|
|
$controller->action_report(); |
|
1670
|
|
|
} |
|
1671
|
|
|
|
|
1672
|
|
|
/** |
|
1673
|
|
|
* List and allow adding/entering all man rules |
|
1674
|
|
|
* |
|
1675
|
|
|
* @uses sub template rules |
|
1676
|
|
|
*/ |
|
1677
|
|
|
public function action_manrules(): void |
|
1678
|
|
|
{ |
|
1679
|
|
|
$controller = new Rules($this->_events); |
|
1680
|
|
|
$controller->setUser($this->user); |
|
1681
|
|
|
$controller->action_rules(); |
|
1682
|
|
|
} |
|
1683
|
|
|
|
|
1684
|
|
|
/** |
|
1685
|
|
|
* Actually do the search of personal messages and show the results |
|
1686
|
|
|
* |
|
1687
|
|
|
* What it does: |
|
1688
|
|
|
* |
|
1689
|
|
|
* - Accessed with ?action=pm;sa=search2 |
|
1690
|
|
|
* - Checks user input and searches the pm table for messages matching the query. |
|
1691
|
|
|
* - Uses the search_results sub template of the PersonalMessage template. |
|
1692
|
|
|
* - Show the results of the search query. |
|
1693
|
|
|
*/ |
|
1694
|
|
|
public function action_search2(): ?bool |
|
1695
|
|
|
{ |
|
1696
|
|
|
$controller = new Search($this->_events); |
|
1697
|
|
|
$controller->setUser($this->user); |
|
1698
|
|
|
|
|
1699
|
|
|
return $controller->action_search2(); |
|
1700
|
|
|
} |
|
1701
|
|
|
|
|
1702
|
|
|
|
|
1703
|
|
|
/** |
|
1704
|
|
|
* Allows searching personal messages. |
|
1705
|
|
|
* |
|
1706
|
|
|
* What it does: |
|
1707
|
|
|
* |
|
1708
|
|
|
* - Accessed with ?action=pm;sa=search |
|
1709
|
|
|
* - Shows the screen to search PMs (?action=pm;sa=search) |
|
1710
|
|
|
* - Uses the search sub template of the PersonalMessage template. |
|
1711
|
|
|
* - Decodes and loads search parameters given in the URL (if any). |
|
1712
|
|
|
* - The form redirects to index.php?action=pm;sa=search2. |
|
1713
|
|
|
* |
|
1714
|
|
|
* @uses search sub template |
|
1715
|
|
|
*/ |
|
1716
|
|
|
public function action_search(): void |
|
1717
|
|
|
{ |
|
1718
|
|
|
$controller = new Search($this->_events); |
|
1719
|
|
|
$controller->setUser($this->user); |
|
1720
|
|
|
$controller->action_search(); |
|
1721
|
|
|
} |
|
1722
|
|
|
|
|
1723
|
|
|
|
|
1724
|
|
|
/** |
|
1725
|
|
|
* Allows the user to mark a personal message as unread, so they remember to come back to it |
|
1726
|
|
|
*/ |
|
1727
|
|
|
public function action_markunread(): void |
|
1728
|
|
|
{ |
|
1729
|
|
|
global $context; |
|
1730
|
|
|
|
|
1731
|
|
|
checkSession('request'); |
|
1732
|
|
|
|
|
1733
|
|
|
$pmsg = $this->_req->getQuery('pmsg', 'intval'); |
|
1734
|
|
|
|
|
1735
|
|
|
// Marking a message as unread, we need a message that was sent to them |
|
1736
|
|
|
// Can't mark your own reply as unread, that would be weird |
|
1737
|
|
|
if (!is_null($pmsg) && checkPMReceived($pmsg)) |
|
1738
|
|
|
{ |
|
1739
|
|
|
// Make sure this is accessible, should be, of course |
|
1740
|
|
|
if (!isAccessiblePM($pmsg, 'inbox')) |
|
1741
|
|
|
{ |
|
1742
|
|
|
throw new Exception('no_access', false); |
|
1743
|
|
|
} |
|
1744
|
|
|
|
|
1745
|
|
|
// Well then, you get to hear about it all over again |
|
1746
|
|
|
markMessagesUnread($pmsg); |
|
1747
|
|
|
} |
|
1748
|
|
|
|
|
1749
|
|
|
// Back to the folder. |
|
1750
|
|
|
redirectexit($context['current_label_redirect']); |
|
1751
|
|
|
} |
|
1752
|
|
|
} |
|
1753
|
|
|
|