1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Handles actions made against a user's profile. |
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
|
|
|
* This file contains code covered by: |
11
|
|
|
* copyright: 2011 Simple Machines (http://www.simplemachines.org) |
12
|
|
|
* |
13
|
|
|
* @version 2.0 dev |
14
|
|
|
* |
15
|
|
|
*/ |
16
|
|
|
|
17
|
|
|
namespace ElkArte\Profile; |
18
|
|
|
|
19
|
|
|
use BBC\ParserWrapper; |
20
|
|
|
use ElkArte\AbstractController; |
21
|
|
|
use ElkArte\Controller\Auth; |
22
|
|
|
use ElkArte\EventManager; |
23
|
|
|
use ElkArte\Exceptions\Exception; |
24
|
|
|
use ElkArte\Languages\Txt; |
25
|
|
|
use ElkArte\Member; |
26
|
|
|
use ElkArte\MembersList; |
27
|
|
|
use ElkArte\User; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* Processes user warnings, account activation and account deletion |
31
|
|
|
*/ |
32
|
|
|
class ProfileAccount extends AbstractController |
33
|
|
|
{ |
34
|
|
|
/** @var int Member id for the account being worked on */ |
35
|
|
|
private $_memID = 0; |
36
|
|
|
|
37
|
|
|
/** @var Member The \ElkArte\Member object is stored here to avoid some global */ |
38
|
|
|
private $_profile; |
39
|
|
|
|
40
|
|
|
/** @var array Holds any errors that were generated when issuing a warning */ |
41
|
|
|
private $_issueErrors = []; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Called before all other methods when coming from the dispatcher or |
45
|
|
|
* action class. |
46
|
|
|
*/ |
47
|
|
|
public function pre_dispatch() |
48
|
|
|
{ |
49
|
|
|
$this->_memID = currentMemberID(); |
50
|
|
|
$this->_profile = MembersList::get($this->_memID); |
|
|
|
|
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Entry point for this class. |
55
|
|
|
* |
56
|
|
|
* @see AbstractController::action_index() |
57
|
|
|
*/ |
58
|
|
|
public function action_index() |
59
|
|
|
{ |
60
|
|
|
// figure out what action to do... if we're called directly |
61
|
|
|
// actions in this class are called from the Profile menu, though. |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Issue/manage a user's warning status. |
66
|
|
|
* |
67
|
|
|
* @throws Exception |
68
|
|
|
* @uses Profile template |
69
|
|
|
* @uses template_issueWarning sub template in ProfileAccount |
70
|
|
|
*/ |
71
|
|
|
public function action_issuewarning() |
72
|
|
|
{ |
73
|
|
|
global $txt, $scripturl, $modSettings, $mbname, $context, $cur_profile; |
74
|
|
|
|
75
|
|
|
// Make sure the sub-template is set... |
76
|
|
|
theme()->getTemplates()->load('ProfileAccount'); |
77
|
|
|
$context['sub_template'] = 'issueWarning'; |
78
|
|
|
|
79
|
|
|
// We need this because of template_load_warning_variables |
80
|
|
|
theme()->getTemplates()->load('Profile'); |
81
|
|
|
loadJavascriptFile('profile.js'); |
82
|
|
|
|
83
|
|
|
// jQuery-UI FTW! |
84
|
|
|
$modSettings['jquery_include_ui'] = true; |
85
|
|
|
loadCSSFile('jquery.ui.slider.css'); |
86
|
|
|
loadCSSFile('jquery.ui.theme.min.css'); |
87
|
|
|
|
88
|
|
|
// Get all the actual settings. |
89
|
|
|
[$modSettings['warning_enable'], $modSettings['user_limit']] = explode(',', $modSettings['warning_settings']); |
90
|
|
|
|
91
|
|
|
// Doesn't hurt to be overly cautious. |
92
|
|
|
if (empty($modSettings['warning_enable']) |
93
|
|
|
|| ($context['user']['is_owner'] && !$cur_profile['warning']) |
94
|
|
|
|| !allowedTo('issue_warning')) |
95
|
|
|
{ |
96
|
|
|
throw new Exception('no_access', false); |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
// Get the base (errors related) stuff done. |
100
|
|
|
Txt::load('Errors'); |
101
|
|
|
$context['custom_error_title'] = $txt['profile_warning_errors_occurred']; |
102
|
|
|
|
103
|
|
|
// Make sure things which are disabled stay disabled. |
104
|
|
|
$modSettings['warning_watch'] = empty($modSettings['warning_watch']) ? 110 : $modSettings['warning_watch']; |
105
|
|
|
$modSettings['warning_moderate'] = !empty($modSettings['warning_moderate']) && !empty($modSettings['postmod_active']) ? $modSettings['warning_moderate'] : 110; |
106
|
|
|
$modSettings['warning_mute'] = empty($modSettings['warning_mute']) ? 110 : $modSettings['warning_mute']; |
107
|
|
|
|
108
|
|
|
$context['warning_limit'] = allowedTo('admin_forum') ? 0 : $modSettings['user_limit']; |
109
|
|
|
$context['member']['warning'] = $cur_profile['warning']; |
110
|
|
|
$context['member']['name'] = $cur_profile['real_name']; |
111
|
|
|
|
112
|
|
|
// What are the limits we can apply? |
113
|
|
|
$context['min_allowed'] = 0; |
114
|
|
|
$context['max_allowed'] = 100; |
115
|
|
|
if ($context['warning_limit'] > 0) |
116
|
|
|
{ |
117
|
|
|
require_once(SUBSDIR . '/Moderation.subs.php'); |
118
|
|
|
$current_applied = warningDailyLimit($this->_memID); |
119
|
|
|
|
120
|
|
|
$context['min_allowed'] = max(0, $cur_profile['warning'] - $current_applied - $context['warning_limit']); |
121
|
|
|
$context['max_allowed'] = min(100, $cur_profile['warning'] - $current_applied + $context['warning_limit']); |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
// Defaults. |
125
|
|
|
$context['warning_data'] = [ |
126
|
|
|
'reason' => '', |
127
|
|
|
'notify' => '', |
128
|
|
|
'notify_subject' => '', |
129
|
|
|
'notify_body' => '', |
130
|
|
|
]; |
131
|
|
|
|
132
|
|
|
// Are we saving? |
133
|
|
|
$this->_save_warning(); |
134
|
|
|
|
135
|
|
|
// Perhaps taking a look first? Good idea that one. |
136
|
|
|
$this->_preview_warning(); |
137
|
|
|
|
138
|
|
|
// If we have errors, lets set them for the template |
139
|
|
|
if (!empty($this->_issueErrors)) |
140
|
|
|
{ |
141
|
|
|
// Fill in the suite of errors. |
142
|
|
|
$context['post_errors'] = []; |
143
|
|
|
foreach ($this->_issueErrors as $error) |
144
|
|
|
{ |
145
|
|
|
$context['post_errors'][] = $txt[$error]; |
146
|
|
|
} |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
$context['page_title'] = $txt['profile_issue_warning']; |
150
|
|
|
|
151
|
|
|
// Let's use a generic list to get all the current warnings |
152
|
|
|
require_once(SUBSDIR . '/Profile.subs.php'); |
153
|
|
|
|
154
|
|
|
// Work our the various levels. |
155
|
|
|
$context['level_effects'] = [ |
156
|
|
|
0 => $txt['profile_warning_effect_none'], |
157
|
|
|
]; |
158
|
|
|
|
159
|
|
|
foreach (['watch', 'moderate', 'mute'] as $status) |
160
|
|
|
{ |
161
|
|
|
if ($modSettings['warning_' . $status] != 110) |
162
|
|
|
{ |
163
|
|
|
$context['level_effects'][$modSettings['warning_' . $status]] = $txt['profile_warning_effect_' . $status]; |
164
|
|
|
} |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
$context['current_level'] = 0; |
168
|
|
|
|
169
|
|
|
foreach ($context['level_effects'] as $limit => $dummy) |
170
|
|
|
{ |
171
|
|
|
if ($context['member']['warning'] >= $limit) |
172
|
|
|
{ |
173
|
|
|
$context['current_level'] = $limit; |
174
|
|
|
} |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
// Build a listing to view the previous warnings for this user |
178
|
|
|
$this->_create_issued_warnings_list(); |
179
|
|
|
|
180
|
|
|
$warning_for_message = $this->_req->getQuery('msg', 'intval', false); |
181
|
|
|
$warned_message_subject = ''; |
182
|
|
|
|
183
|
|
|
// Are they warning because of a message? |
184
|
|
|
if (!empty($warning_for_message) && $warning_for_message > 0) |
185
|
|
|
{ |
186
|
|
|
require_once(SUBSDIR . '/Messages.subs.php'); |
187
|
|
|
$message = basicMessageInfo($warning_for_message); |
188
|
|
|
|
189
|
|
|
if (!empty($message)) |
190
|
|
|
{ |
191
|
|
|
$warned_message_subject = $message['subject']; |
192
|
|
|
} |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
require_once(SUBSDIR . '/Maillist.subs.php'); |
196
|
|
|
|
197
|
|
|
// Any custom templates? |
198
|
|
|
$context['notification_templates'] = []; |
199
|
|
|
$notification_templates = maillist_templates('warntpl'); |
200
|
|
|
|
201
|
|
|
foreach ($notification_templates as $row) |
202
|
|
|
{ |
203
|
|
|
// If we're not warning for a message skip any that are. |
204
|
|
|
if ($warning_for_message === false && strpos($row['body'], '{MESSAGE}') !== false) |
205
|
|
|
{ |
206
|
|
|
continue; |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
$context['notification_templates'][] = [ |
210
|
|
|
'title' => $row['title'], |
211
|
|
|
'body' => $row['body'], |
212
|
|
|
]; |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
// Setup the "default" templates. |
216
|
|
|
foreach (['spamming', 'offence', 'insulting'] as $type) |
217
|
|
|
{ |
218
|
|
|
$context['notification_templates'][] = [ |
219
|
|
|
'title' => $txt['profile_warning_notify_title_' . $type], |
220
|
|
|
'body' => sprintf($txt['profile_warning_notify_template_outline' . (empty($warning_for_message) ? '' : '_post')], $txt['profile_warning_notify_for_' . $type]), |
221
|
|
|
]; |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
// Replace all the common variables in the templates. |
225
|
|
|
foreach ($context['notification_templates'] as $k => $name) |
226
|
|
|
{ |
227
|
|
|
$context['notification_templates'][$k]['body'] = strtr($name['body'], |
228
|
|
|
[ |
229
|
|
|
'{MEMBER}' => un_htmlspecialchars($context['member']['name']), |
230
|
|
|
'{MESSAGE}' => '[url=' . getUrl('action', ['msg' => $warning_for_message]) . ']' . un_htmlspecialchars($warned_message_subject) . '[/url]', |
231
|
|
|
'{SCRIPTURL}' => $scripturl, |
232
|
|
|
'{FORUMNAME}' => $mbname, |
233
|
|
|
'{REGARDS}' => replaceBasicActionUrl($txt['regards_team']) |
234
|
|
|
] |
235
|
|
|
); |
236
|
|
|
} |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* Does the actual issuing of a warning to a member |
241
|
|
|
* |
242
|
|
|
* What it does: |
243
|
|
|
* |
244
|
|
|
* - Validates the inputs |
245
|
|
|
* - Sends the warning PM if required, to the member |
246
|
|
|
* - Logs the action |
247
|
|
|
* - Updates the user data with the new warning level |
248
|
|
|
*/ |
249
|
|
|
private function _save_warning() |
250
|
|
|
{ |
251
|
|
|
global $txt, $context, $cur_profile; |
252
|
|
|
|
253
|
|
|
if (isset($this->_req->post->save)) |
254
|
|
|
{ |
255
|
|
|
// Security is good here. |
256
|
|
|
checkSession(); |
257
|
|
|
|
258
|
|
|
// There must be a reason, and use of flowery words is allowed. |
259
|
|
|
$warn_reason = $this->_req->getPost('warn_reason', 'trim|\\ElkArte\\Helper\\Util::htmlspecialchars', ''); |
260
|
|
|
if ($warn_reason === '' && !$context['user']['is_owner']) |
261
|
|
|
{ |
262
|
|
|
$this->_issueErrors[] = 'warning_no_reason'; |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
// If the value hasn't changed it's either no JS or a real no change (Which this will pass) |
266
|
|
|
if ($warn_reason === 'SAME') |
267
|
|
|
{ |
268
|
|
|
$this->_req->post->warning_level = $this->_req->post->warning_level_nojs; |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
// Set and contain the level and level changes |
272
|
|
|
$warning_level = (int) $this->_req->post->warning_level; |
273
|
|
|
$warning_level = max(0, min(100, $warning_level)); |
274
|
|
|
|
275
|
|
|
if ($warning_level < $context['min_allowed']) |
276
|
|
|
{ |
277
|
|
|
$warning_level = $context['min_allowed']; |
278
|
|
|
} |
279
|
|
|
elseif ($warning_level > $context['max_allowed']) |
280
|
|
|
{ |
281
|
|
|
$warning_level = $context['max_allowed']; |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
// We need this to log moderation notices |
285
|
|
|
require_once(SUBSDIR . '/Moderation.subs.php'); |
286
|
|
|
|
287
|
|
|
// Do we actually have to issue them with a PM? |
288
|
|
|
$id_notice = $this->_issue_warning_pm(); |
289
|
|
|
|
290
|
|
|
// What have we changed? |
291
|
|
|
$level_change = $warning_level - $cur_profile['warning']; |
292
|
|
|
|
293
|
|
|
// No errors? Proceed! Only log if you're not the owner. |
294
|
|
|
if (empty($this->_issueErrors)) |
295
|
|
|
{ |
296
|
|
|
// Log what we've done! |
297
|
|
|
if (!$context['user']['is_owner']) |
298
|
|
|
{ |
299
|
|
|
logWarning($this->_memID, $cur_profile['real_name'], $id_notice, $level_change, $warn_reason); |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
// Make the change. |
303
|
|
|
require_once(SUBSDIR . '/Members.subs.php'); |
304
|
|
|
updateMemberData($this->_memID, ['warning' => $warning_level]); |
305
|
|
|
|
306
|
|
|
// Leave a lovely message. |
307
|
|
|
$context['profile_updated'] = $context['user']['is_owner'] ? $txt['profile_updated_own'] : $txt['profile_warning_success']; |
308
|
|
|
} |
309
|
|
|
else |
310
|
|
|
{ |
311
|
|
|
// Try to remember some bits. |
312
|
|
|
$context['warning_data'] = [ |
313
|
|
|
'reason' => $warn_reason, |
314
|
|
|
'notify' => !empty($this->_req->post->warn_notify), |
315
|
|
|
'notify_subject' => $this->_req->getPost('warn_sub', 'trim', ''), |
316
|
|
|
'notify_body' => $this->_req->getPost('warn_body', 'trim', ''), |
317
|
|
|
]; |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
// Show the new improved warning level. |
321
|
|
|
$context['member']['warning'] = $warning_level; |
322
|
|
|
} |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
/** |
326
|
|
|
* Issue a pm to the member getting the warning |
327
|
|
|
* |
328
|
|
|
* @return int |
329
|
|
|
* @throws Exception |
330
|
|
|
*/ |
331
|
|
|
private function _issue_warning_pm() |
332
|
|
|
{ |
333
|
|
|
global $context, $modSettings; |
334
|
|
|
|
335
|
|
|
$id_notice = 0; |
336
|
|
|
if (!empty($this->_req->post->warn_notify) && empty($this->_issueErrors)) |
337
|
|
|
{ |
338
|
|
|
$warn_sub = $this->_req->getPost('warn_sub', 'trim', ''); |
339
|
|
|
$warn_body = $this->_req->getPost('warn_body', 'trim', ''); |
340
|
|
|
|
341
|
|
|
if (empty($warn_sub) || empty($warn_body)) |
342
|
|
|
{ |
343
|
|
|
$this->_issueErrors[] = 'warning_notify_blank'; |
344
|
|
|
} |
345
|
|
|
// Send the PM? |
346
|
|
|
else |
347
|
|
|
{ |
348
|
|
|
require_once(SUBSDIR . '/PersonalMessage.subs.php'); |
349
|
|
|
$from = [ |
350
|
|
|
'id' => 0, |
351
|
|
|
'name' => $context['forum_name'], |
352
|
|
|
'username' => $context['forum_name'], |
353
|
|
|
]; |
354
|
|
|
|
355
|
|
|
// No sense in sending a PbE |
356
|
|
|
$modSettings['maillist_enabled'] = 0; |
357
|
|
|
sendpm(['to' => [$this->_memID], 'bcc' => []], $warn_sub, $warn_body, false, $from); |
358
|
|
|
|
359
|
|
|
// Log the notice. |
360
|
|
|
$id_notice = logWarningNotice($warn_sub, $warn_body); |
361
|
|
|
} |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
return $id_notice; |
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
/** |
368
|
|
|
* Prepares a warning preview |
369
|
|
|
*/ |
370
|
|
|
private function _preview_warning() |
371
|
|
|
{ |
372
|
|
|
global $context; |
373
|
|
|
|
374
|
|
|
if (isset($this->_req->post->preview)) |
375
|
|
|
{ |
376
|
|
|
$warning_body = empty($this->_req->post->warn_body) ? '' : trim(censor($this->_req->post->warn_body)); |
377
|
|
|
|
378
|
|
|
if (empty($this->_req->post->warn_sub) || empty($this->_req->post->warn_body)) |
379
|
|
|
{ |
380
|
|
|
$this->_issueErrors[] = 'warning_notify_blank'; |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
if (!empty($this->_req->post->warn_body)) |
384
|
|
|
{ |
385
|
|
|
require_once(SUBSDIR . '/Post.subs.php'); |
386
|
|
|
|
387
|
|
|
$bbc_parser = ParserWrapper::instance(); |
388
|
|
|
preparsecode($warning_body); |
389
|
|
|
$warning_body = $bbc_parser->parseNotice($warning_body); |
390
|
|
|
} |
391
|
|
|
|
392
|
|
|
// Try to remember some bits. |
393
|
|
|
$context['preview_subject'] = $this->_req->getPost('warn_sub', 'trim|\\ElkArte\\Helper\\Util::htmlspecialchars', ''); |
394
|
|
|
$context['warning_data'] = [ |
395
|
|
|
'reason' => $this->_req->post->warn_reason, |
396
|
|
|
'notify' => !empty($this->_req->post->warn_notify), |
397
|
|
|
'notify_subject' => $this->_req->getPost('warn_sub', 'trim', ''), |
398
|
|
|
'notify_body' => $this->_req->getPost('warn_body', 'trim', ''), |
399
|
|
|
'body_preview' => $warning_body, |
400
|
|
|
]; |
401
|
|
|
} |
402
|
|
|
} |
403
|
|
|
|
404
|
|
|
/** |
405
|
|
|
* Creates the listing of issued warnings |
406
|
|
|
*/ |
407
|
|
|
private function _create_issued_warnings_list() |
408
|
|
|
{ |
409
|
|
|
global $txt, $modSettings; |
410
|
|
|
|
411
|
|
|
$listOptions = [ |
412
|
|
|
'id' => 'issued_warnings', |
413
|
|
|
'title' => $txt['profile_viewwarning_previous_warnings'], |
414
|
|
|
'items_per_page' => $modSettings['defaultMaxMessages'], |
415
|
|
|
'no_items_label' => $txt['profile_viewwarning_no_warnings'], |
416
|
|
|
'base_href' => getUrl('action', ['action' => 'profile', 'area' => 'issuewarning', 'sa' => 'user', 'u' => $this->_memID]), |
417
|
|
|
'default_sort_col' => 'log_time', |
418
|
|
|
'get_items' => [ |
419
|
|
|
'function' => fn($start, $items_per_page, $sort) => $this->list_getUserWarnings($start, $items_per_page, $sort), |
420
|
|
|
], |
421
|
|
|
'get_count' => [ |
422
|
|
|
'function' => fn() => $this->list_getUserWarningCount(), |
423
|
|
|
], |
424
|
|
|
'columns' => [ |
425
|
|
|
'issued_by' => [ |
426
|
|
|
'header' => [ |
427
|
|
|
'value' => $txt['profile_warning_previous_issued'], |
428
|
|
|
'class' => 'grid20', |
429
|
|
|
], |
430
|
|
|
'data' => [ |
431
|
|
|
'function' => static fn($warning) => $warning['issuer']['link'], |
432
|
|
|
], |
433
|
|
|
'sort' => [ |
434
|
|
|
'default' => 'lc.member_name DESC', |
435
|
|
|
'reverse' => 'lc.member_name', |
436
|
|
|
], |
437
|
|
|
], |
438
|
|
|
'log_time' => [ |
439
|
|
|
'header' => [ |
440
|
|
|
'value' => $txt['profile_warning_previous_time'], |
441
|
|
|
'class' => 'grid30', |
442
|
|
|
], |
443
|
|
|
'data' => [ |
444
|
|
|
'db' => 'time', |
445
|
|
|
], |
446
|
|
|
'sort' => [ |
447
|
|
|
'default' => 'lc.log_time DESC', |
448
|
|
|
'reverse' => 'lc.log_time', |
449
|
|
|
], |
450
|
|
|
], |
451
|
|
|
'reason' => [ |
452
|
|
|
'header' => [ |
453
|
|
|
'value' => $txt['profile_warning_previous_reason'], |
454
|
|
|
], |
455
|
|
|
'data' => [ |
456
|
|
|
'function' => static function ($warning) { |
457
|
|
|
global $txt; |
458
|
|
|
|
459
|
|
|
$ret = ' |
460
|
|
|
<div class="floatleft"> |
461
|
|
|
' . $warning['reason'] . ' |
462
|
|
|
</div>'; |
463
|
|
|
|
464
|
|
|
// If a notice was sent, provide a way to view it |
465
|
|
|
if (!empty($warning['id_notice'])) |
466
|
|
|
{ |
467
|
|
|
$ret .= ' |
468
|
|
|
<div class="floatright"> |
469
|
|
|
<a href="' . getUrl('action', ['action' => 'moderate', 'area' => 'notice', 'nid' => $warning['id_notice']]) . '" onclick="window.open(this.href, \'\', \'scrollbars=yes,resizable=yes,width=400,height=250\');return false;" target="_blank" class="new_win" title="' . $txt['profile_warning_previous_notice'] . '"><i class="icon icon-small i-search"></i></a> |
470
|
|
|
</div>'; |
471
|
|
|
} |
472
|
|
|
|
473
|
|
|
return $ret; |
474
|
|
|
}, |
475
|
|
|
], |
476
|
|
|
], |
477
|
|
|
'level' => [ |
478
|
|
|
'header' => [ |
479
|
|
|
'value' => $txt['profile_warning_previous_level'], |
480
|
|
|
'class' => 'grid8', |
481
|
|
|
], |
482
|
|
|
'data' => [ |
483
|
|
|
'db' => 'counter', |
484
|
|
|
], |
485
|
|
|
'sort' => [ |
486
|
|
|
'default' => 'lc.counter DESC', |
487
|
|
|
'reverse' => 'lc.counter', |
488
|
|
|
], |
489
|
|
|
], |
490
|
|
|
], |
491
|
|
|
]; |
492
|
|
|
|
493
|
|
|
// Create the list for viewing. |
494
|
|
|
createList($listOptions); |
495
|
|
|
} |
496
|
|
|
|
497
|
|
|
/** |
498
|
|
|
* Callback for createList(). Called by action_hooks |
499
|
|
|
* |
500
|
|
|
* @param int $start The item to start with (for pagination purposes) |
501
|
|
|
* @param int $items_per_page The number of items to show per page |
502
|
|
|
* @param string $sort A string indicating how to sort the results |
503
|
|
|
* |
504
|
|
|
* @return array |
505
|
|
|
*/ |
506
|
|
|
public function list_getUserWarnings($start, $items_per_page, $sort) |
507
|
|
|
{ |
508
|
|
|
return list_getUserWarnings($start, $items_per_page, $sort, $this->_memID); |
509
|
|
|
} |
510
|
|
|
|
511
|
|
|
/** |
512
|
|
|
* Simply returns the total count of warnings |
513
|
|
|
* Callback for createList(). |
514
|
|
|
* |
515
|
|
|
* @return int |
516
|
|
|
*/ |
517
|
|
|
public function list_getUserWarningCount() |
518
|
|
|
{ |
519
|
|
|
return list_getUserWarningCount($this->_memID); |
520
|
|
|
} |
521
|
|
|
|
522
|
|
|
/** |
523
|
|
|
* Present a screen to make sure the user wants to be deleted. |
524
|
|
|
*/ |
525
|
|
|
public function action_deleteaccount() |
526
|
|
|
{ |
527
|
|
|
global $txt, $context, $modSettings, $cur_profile; |
528
|
|
|
|
529
|
|
|
if (!$context['user']['is_owner']) |
530
|
|
|
{ |
531
|
|
|
isAllowedTo('profile_remove_any'); |
532
|
|
|
} |
533
|
|
|
elseif (!allowedTo('profile_remove_any')) |
534
|
|
|
{ |
535
|
|
|
isAllowedTo('profile_remove_own'); |
536
|
|
|
} |
537
|
|
|
|
538
|
|
|
// Permissions for removing stuff... |
539
|
|
|
$context['can_delete_posts'] = !$context['user']['is_owner'] && allowedTo('moderate_forum'); |
540
|
|
|
|
541
|
|
|
// Can they do this, or will they need approval? |
542
|
|
|
$context['needs_approval'] = $context['user']['is_owner'] && !empty($modSettings['approveAccountDeletion']) && !allowedTo('moderate_forum'); |
543
|
|
|
$context['page_title'] = $txt['deleteAccount'] . ': ' . $cur_profile['real_name']; |
544
|
|
|
|
545
|
|
|
// make sure the sub-template is set... |
546
|
|
|
theme()->getTemplates()->load('ProfileAccount'); |
547
|
|
|
$context['sub_template'] = 'deleteAccount'; |
548
|
|
|
} |
549
|
|
|
|
550
|
|
|
/** |
551
|
|
|
* Actually delete an account. |
552
|
|
|
*/ |
553
|
|
|
public function action_deleteaccount2() |
554
|
|
|
{ |
555
|
|
|
global $context, $cur_profile, $modSettings; |
556
|
|
|
|
557
|
|
|
// Try get more time... |
558
|
|
|
detectServer()->setTimeLimit(600); |
559
|
|
|
|
560
|
|
|
// @todo Add a way to delete pms as well? |
561
|
|
|
if (!$context['user']['is_owner']) |
562
|
|
|
{ |
563
|
|
|
isAllowedTo('profile_remove_any'); |
564
|
|
|
} |
565
|
|
|
elseif (!allowedTo('profile_remove_any')) |
566
|
|
|
{ |
567
|
|
|
isAllowedTo('profile_remove_own'); |
568
|
|
|
} |
569
|
|
|
|
570
|
|
|
checkSession(); |
571
|
|
|
|
572
|
|
|
// Check we got here as we should have! |
573
|
|
|
if ((int) $cur_profile->id_member !== (int) $this->_profile->id_member) |
574
|
|
|
{ |
575
|
|
|
throw new Exception('no_access', false); |
576
|
|
|
} |
577
|
|
|
|
578
|
|
|
// This file is needed for our utility functions. |
579
|
|
|
require_once(SUBSDIR . '/Members.subs.php'); |
580
|
|
|
|
581
|
|
|
// Too often, people remove/delete their own only administrative account. |
582
|
|
|
if (in_array(1, array_map('intval', explode(',', $cur_profile['additional_groups'])), true) || (int) $cur_profile['id_group'] === 1) |
583
|
|
|
{ |
584
|
|
|
// Are you allowed to administrate the forum, as they are? |
585
|
|
|
isAllowedTo('admin_forum'); |
586
|
|
|
|
587
|
|
|
$another = isAnotherAdmin($this->_memID); |
588
|
|
|
|
589
|
|
|
if (empty($another)) |
590
|
|
|
{ |
591
|
|
|
throw new Exception('at_least_one_admin', 'critical'); |
592
|
|
|
} |
593
|
|
|
} |
594
|
|
|
|
595
|
|
|
// Do you have permission to delete others profiles, or is that your profile you wanna delete? |
596
|
|
|
if ($this->_memID != $this->user->id) |
597
|
|
|
{ |
598
|
|
|
isAllowedTo('profile_remove_any'); |
599
|
|
|
|
600
|
|
|
// Now, have you been naughty and need your posts deleting? |
601
|
|
|
// @todo Should this check board permissions? |
602
|
|
|
if ($this->_req->post->remove_type !== 'none' && allowedTo('moderate_forum')) |
603
|
|
|
{ |
604
|
|
|
// Include subs/Topic.subs.php - essential for this type of work! |
605
|
|
|
require_once(SUBSDIR . '/Topic.subs.php'); |
606
|
|
|
require_once(SUBSDIR . '/Messages.subs.php'); |
607
|
|
|
|
608
|
|
|
// First off we delete any topics the member has started - if they wanted topics being done. |
609
|
|
|
if ($this->_req->post->remove_type === 'topics') |
610
|
|
|
{ |
611
|
|
|
// Fetch all topics started by this user. |
612
|
|
|
$topicIDs = topicsStartedBy($this->_memID); |
613
|
|
|
|
614
|
|
|
// Actually remove the topics. |
615
|
|
|
// @todo This needs to check permissions, but we'll let it slide for now because of moderate_forum already being had. |
616
|
|
|
removeTopics($topicIDs); |
617
|
|
|
} |
618
|
|
|
|
619
|
|
|
// Now delete the remaining messages. |
620
|
|
|
removeNonTopicMessages($this->_memID); |
621
|
|
|
} |
622
|
|
|
|
623
|
|
|
// Only delete this poor member's account if they are actually being booted out of camp. |
624
|
|
|
if (isset($this->_req->post->deleteAccount)) |
625
|
|
|
{ |
626
|
|
|
deleteMembers($this->_memID); |
627
|
|
|
} |
628
|
|
|
} |
629
|
|
|
// Do they need approval to delete? |
630
|
|
|
elseif (!empty($modSettings['approveAccountDeletion']) && !allowedTo('moderate_forum')) |
631
|
|
|
{ |
632
|
|
|
// Setup their account for deletion ;) |
633
|
|
|
require_once(SUBSDIR . '/Members.subs.php'); |
634
|
|
|
updateMemberData($this->_memID, ['is_activated' => 4]); |
635
|
|
|
|
636
|
|
|
// Another account needs approval... |
637
|
|
|
updateSettings(['unapprovedMembers' => true], true); |
638
|
|
|
} |
639
|
|
|
// Also check if you typed your password correctly. |
640
|
|
|
else |
641
|
|
|
{ |
642
|
|
|
deleteMembers($this->_memID); |
643
|
|
|
|
644
|
|
|
$controller = new Auth(new EventManager()); |
645
|
|
|
$controller->setUser(User::$info); |
646
|
|
|
$controller->action_logout(true); |
647
|
|
|
|
648
|
|
|
redirectexit(); |
649
|
|
|
} |
650
|
|
|
} |
651
|
|
|
|
652
|
|
|
/** |
653
|
|
|
* Activate an account. |
654
|
|
|
* |
655
|
|
|
* - This function is called from the profile account actions area. |
656
|
|
|
*/ |
657
|
|
|
public function action_activateaccount() |
658
|
|
|
{ |
659
|
|
|
global $context, $modSettings; |
660
|
|
|
|
661
|
|
|
isAllowedTo('moderate_forum'); |
662
|
|
|
|
663
|
|
|
$this->_profile['is_activated'] = (int) $this->_profile['is_activated']; |
664
|
|
|
if (isset($this->_req->query->save, $this->_profile['is_activated']) |
665
|
|
|
&& $this->_profile['is_activated'] !== 1) |
666
|
|
|
{ |
667
|
|
|
require_once(SUBSDIR . '/Members.subs.php'); |
668
|
|
|
|
669
|
|
|
// If we are approving the deletion of an account, we do something special ;) |
670
|
|
|
if ($this->_profile['is_activated'] === 4) |
671
|
|
|
{ |
672
|
|
|
deleteMembers($context['id_member']); |
673
|
|
|
redirectexit(); |
674
|
|
|
} |
675
|
|
|
|
676
|
|
|
// Actually update this member now, as it guarantees the unapproved count can't get corrupted. |
677
|
|
|
approveMembers(['members' => [$context['id_member']], 'activated_status' => $this->_profile['is_activated']]); |
678
|
|
|
|
679
|
|
|
// Log what we did? |
680
|
|
|
logAction('approve_member', ['member' => $this->_memID], 'admin'); |
681
|
|
|
|
682
|
|
|
// If we are doing approval, update the stats for the member just in case. |
683
|
|
|
if (in_array($this->_profile['is_activated'], [3, 4, 5, 13, 14, 15])) |
684
|
|
|
{ |
685
|
|
|
updateSettings(['unapprovedMembers' => ($modSettings['unapprovedMembers'] > 1 ? $modSettings['unapprovedMembers'] - 1 : 0)]); |
686
|
|
|
} |
687
|
|
|
|
688
|
|
|
// Make sure we update the stats too. |
689
|
|
|
require_once(SUBSDIR . '/Members.subs.php'); |
690
|
|
|
updateMemberStats(); |
691
|
|
|
} |
692
|
|
|
|
693
|
|
|
// Leave it be... |
694
|
|
|
redirectexit('action=profile;u=' . $this->_memID . ';area=summary'); |
695
|
|
|
} |
696
|
|
|
} |
697
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.