1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Show a list of members or a selection of members. |
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\AdminController; |
18
|
|
|
|
19
|
|
|
use ElkArte\AbstractController; |
20
|
|
|
use ElkArte\Action; |
21
|
|
|
use ElkArte\Cache\Cache; |
22
|
|
|
use ElkArte\Helper\Util; |
23
|
|
|
use ElkArte\Languages\Txt; |
24
|
|
|
use ElkArte\User; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* ManageMembers controller deals with members administration, approval, |
28
|
|
|
* admin-visible list and search in it. |
29
|
|
|
* |
30
|
|
|
* @package Members |
31
|
|
|
*/ |
32
|
|
|
class ManageMembers extends AbstractController |
33
|
|
|
{ |
34
|
|
|
/** @var array Holds various setting conditions for the current action */ |
35
|
|
|
protected $conditions; |
36
|
|
|
|
37
|
|
|
/** @var array Holds the members that the action is being applied to */ |
38
|
|
|
protected $member_info; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* The main entrance point for the Manage Members screen. |
42
|
|
|
* |
43
|
|
|
* What it does: |
44
|
|
|
* |
45
|
|
|
* - As everywhere else, it calls a function based on the given sub-action. |
46
|
|
|
* - Called by ?action=admin;area=viewmembers. |
47
|
|
|
* - Requires the moderate_forum permission. |
48
|
|
|
* |
49
|
|
|
* @event integrate_manage_members used to add subactions and tabs |
50
|
|
|
* @uses ManageMembers template |
51
|
|
|
* @uses ManageMembers language file. |
52
|
|
|
* @see AbstractCowntroller::action_index() |
53
|
|
|
*/ |
54
|
|
|
public function action_index() |
55
|
|
|
{ |
56
|
|
|
global $txt, $context, $modSettings; |
57
|
|
|
|
58
|
|
|
// Load the essentials. |
59
|
|
|
Txt::load('ManageMembers'); |
60
|
|
|
theme()->getTemplates()->load('ManageMembers'); |
61
|
|
|
|
62
|
|
|
$subActions = array( |
63
|
|
|
'all' => array( |
64
|
|
|
'controller' => $this, |
65
|
|
|
'function' => 'action_list', |
66
|
|
|
'permission' => 'moderate_forum'), |
67
|
|
|
'approve' => array( |
68
|
|
|
'controller' => $this, |
69
|
|
|
'function' => 'action_approve', |
70
|
|
|
'permission' => 'moderate_forum'), |
71
|
|
|
'browse' => array( |
72
|
|
|
'controller' => $this, |
73
|
|
|
'function' => 'action_browse', |
74
|
|
|
'permission' => 'moderate_forum'), |
75
|
|
|
'search' => array( |
76
|
|
|
'controller' => $this, |
77
|
|
|
'function' => 'action_search', |
78
|
|
|
'permission' => 'moderate_forum'), |
79
|
|
|
'query' => array( |
80
|
|
|
'controller' => $this, |
81
|
|
|
'function' => 'action_list', |
82
|
|
|
'permission' => 'moderate_forum'), |
83
|
|
|
); |
84
|
|
|
|
85
|
|
|
// Prepare our action control |
86
|
|
|
$action = new Action(); |
87
|
|
|
|
88
|
|
|
// Default to sub action 'all', needed for the tabs array below |
89
|
|
|
$subAction = $action->initialize($subActions, 'all'); |
90
|
|
|
|
91
|
|
|
// Get counts on every type of activation - for sections and filtering alike. |
92
|
|
|
require_once(SUBSDIR . '/Members.subs.php'); |
93
|
|
|
|
94
|
|
|
$context['awaiting_activation'] = 0; |
95
|
|
|
$context['awaiting_approval'] = 0; |
96
|
|
|
$context['activation_numbers'] = countInactiveMembers(); |
97
|
|
|
|
98
|
|
|
foreach ($context['activation_numbers'] as $activation_type => $total_members) |
99
|
|
|
{ |
100
|
|
|
if (in_array($activation_type, array(0, 2), true)) |
101
|
|
|
{ |
102
|
|
|
$context['awaiting_activation'] += $total_members; |
103
|
|
|
} |
104
|
|
|
elseif (in_array($activation_type, array(3, 4, 5), true)) |
105
|
|
|
{ |
106
|
|
|
$context['awaiting_approval'] += $total_members; |
107
|
|
|
} |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
// Last items for the template |
111
|
|
|
$context['page_title'] = $txt['admin_members']; |
112
|
|
|
$context['sub_action'] = $subAction; |
113
|
|
|
|
114
|
|
|
// For the page header... do we show activation? |
115
|
|
|
$context['show_activate'] = (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 1) || !empty($context['awaiting_activation']); |
116
|
|
|
|
117
|
|
|
// What about approval? |
118
|
|
|
$context['show_approve'] = (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 2) || !empty($context['awaiting_approval']) || !empty($modSettings['approveAccountDeletion']); |
119
|
|
|
|
120
|
|
|
// Setup the admin tabs. |
121
|
|
|
$context[$context['admin_menu_name']]['object']->prepareTabData([ |
122
|
|
|
'title' => 'admin_members', |
123
|
|
|
'help' => 'view_members', |
124
|
|
|
'description' => 'admin_members_list', |
125
|
|
|
'tabs' => [ |
126
|
|
|
'viewmembers' => [ |
127
|
|
|
'label' => $txt['view_all_members'], |
128
|
|
|
'description' => $txt['admin_members_list'], |
129
|
|
|
'url' => getUrl('admin', ['action' => 'admin', 'area' => 'viewmembers', 'sa' => 'all']), |
130
|
|
|
'selected' => $subAction === 'all', |
131
|
|
|
], |
132
|
|
|
'search' => [ |
133
|
|
|
'label' => $txt['mlist_search'], |
134
|
|
|
'description' => $txt['admin_members_list'], |
135
|
|
|
'url' => getUrl('admin', ['action' => 'admin', 'area' => 'viewmembers', 'sa' => 'search']), |
136
|
|
|
'selected' => $subAction === 'search' || $subAction === 'query', |
137
|
|
|
], |
138
|
|
|
'approve' => [ |
139
|
|
|
'label' => sprintf($txt['admin_browse_awaiting_approval'], $context['awaiting_approval']), |
140
|
|
|
'description' => $txt['admin_browse_approve_desc'], |
141
|
|
|
'url' => getUrl('admin', ['action' => 'admin', 'area' => 'viewmembers', 'sa' => 'browse', 'type' => 'approve']), |
142
|
|
|
'disabled' => !$context['show_approve'] && ($subAction !== 'browse' || $this->_req->getQuery('type') !== 'approve'), |
143
|
|
|
'selected' => $subAction === 'browse' && $this->_req->getQuery('type') === 'approve', |
144
|
|
|
], |
145
|
|
|
'activate' => [ |
146
|
|
|
'label' => sprintf($txt['admin_browse_awaiting_activate'], $context['awaiting_activation']), |
147
|
|
|
'description' => $txt['admin_browse_activate_desc'], |
148
|
|
|
'url' => getUrl('admin', ['action' => 'admin', 'area' => 'viewmembers', 'sa' => 'browse', 'type' => 'activate']), |
149
|
|
|
'disabled' => !$context['show_activate'] && ($subAction !== 'browse' || $this->_req->query->type !== 'activate'), |
150
|
|
|
'selected' => $subAction === 'browse' && $this->_req->getQuery('type') === 'activate', |
151
|
|
|
], |
152
|
|
|
] |
153
|
|
|
]); |
154
|
|
|
|
155
|
|
|
// Call integrate_manage_members |
156
|
|
|
call_integration_hook('integrate_manage_members', array(&$subActions)); |
157
|
|
|
|
158
|
|
|
// Off we go |
159
|
|
|
$action->dispatch($subAction); |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* View all members list. It allows sorting on several columns, and deletion of |
164
|
|
|
* selected members. |
165
|
|
|
* |
166
|
|
|
* - It also handles the search query sent by ?action=admin;area=viewmembers;sa=search. |
167
|
|
|
* - Called by ?action=admin;area=viewmembers;sa=all or ?action=admin;area=viewmembers;sa=query. |
168
|
|
|
* - Requires the moderate_forum permission. |
169
|
|
|
* |
170
|
|
|
* @event integrate_list_member_list |
171
|
|
|
* @event integrate_view_members_params passed $params |
172
|
|
|
* @uses the view_members sub template of the ManageMembers template. |
173
|
|
|
*/ |
174
|
|
|
public function action_list() |
175
|
|
|
{ |
176
|
|
|
global $txt, $context, $modSettings; |
177
|
|
|
|
178
|
|
|
// Set the current sub action. |
179
|
|
|
$context['sub_action'] = $this->_req->getQuery('sa', 'strval', $this->_req->getPost('sa', 'strval', 'all')); |
180
|
|
|
|
181
|
|
|
// Are we performing a mass action? |
182
|
|
|
if (isset($this->_req->post->maction_on_members, $this->_req->post->maction) && !empty($this->_req->post->members)) |
183
|
|
|
{ |
184
|
|
|
$this->_multiMembersAction(); |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
// Check input after a member search has been submitted. |
188
|
|
|
if ($context['sub_action'] === 'query') |
189
|
|
|
{ |
190
|
|
|
// Retrieving the membergroups and postgroups. |
191
|
|
|
require_once(SUBSDIR . '/Membergroups.subs.php'); |
192
|
|
|
$groups = getBasicMembergroupData(array(), array('moderator'), null, true); |
193
|
|
|
|
194
|
|
|
$context['membergroups'] = $groups['membergroups']; |
195
|
|
|
$context['postgroups'] = $groups['groups']; |
196
|
|
|
unset($groups); |
197
|
|
|
|
198
|
|
|
// Some data about the form fields and how they are linked to the database. |
199
|
|
|
$params = array( |
200
|
|
|
'mem_id' => array( |
201
|
|
|
'db_fields' => array('id_member'), |
202
|
|
|
'type' => 'int', |
203
|
|
|
'range' => true |
204
|
|
|
), |
205
|
|
|
'age' => array( |
206
|
|
|
'db_fields' => array('birthdate'), |
207
|
|
|
'type' => 'age', |
208
|
|
|
'range' => true |
209
|
|
|
), |
210
|
|
|
'posts' => array( |
211
|
|
|
'db_fields' => array('posts'), |
212
|
|
|
'type' => 'int', |
213
|
|
|
'range' => true |
214
|
|
|
), |
215
|
|
|
'reg_date' => array( |
216
|
|
|
'db_fields' => array('date_registered'), |
217
|
|
|
'type' => 'date', |
218
|
|
|
'range' => true |
219
|
|
|
), |
220
|
|
|
'last_online' => array( |
221
|
|
|
'db_fields' => array('last_login'), |
222
|
|
|
'type' => 'date', |
223
|
|
|
'range' => true |
224
|
|
|
), |
225
|
|
|
'activated' => array( |
226
|
|
|
'db_fields' => array('is_activated'), |
227
|
|
|
'type' => 'checkbox', |
228
|
|
|
'values' => array('0', '1', '11'), |
229
|
|
|
), |
230
|
|
|
'membername' => array( |
231
|
|
|
'db_fields' => array('member_name', 'real_name'), |
232
|
|
|
'type' => 'string' |
233
|
|
|
), |
234
|
|
|
'email' => array( |
235
|
|
|
'db_fields' => array('email_address'), |
236
|
|
|
'type' => 'string' |
237
|
|
|
), |
238
|
|
|
'website' => array( |
239
|
|
|
'db_fields' => array('website_title', 'website_url'), |
240
|
|
|
'type' => 'string' |
241
|
|
|
), |
242
|
|
|
'ip' => array( |
243
|
|
|
'db_fields' => array('member_ip'), |
244
|
|
|
'type' => 'string' |
245
|
|
|
) |
246
|
|
|
); |
247
|
|
|
|
248
|
|
|
$range_trans = array( |
249
|
|
|
'--' => '<', |
250
|
|
|
'-' => '<=', |
251
|
|
|
'=' => '=', |
252
|
|
|
'+' => '>=', |
253
|
|
|
'++' => '>' |
254
|
|
|
); |
255
|
|
|
|
256
|
|
|
call_integration_hook('integrate_view_members_params', array(&$params)); |
257
|
|
|
|
258
|
|
|
$search_params = array(); |
259
|
|
|
if ($context['sub_action'] === 'query' && !empty($this->_req->query->params) && empty($this->_req->post->types)) |
260
|
|
|
{ |
261
|
|
|
$search_params = @json_decode(base64_decode($this->_req->query->params), true); |
262
|
|
|
} |
263
|
|
|
elseif (!empty($this->_req->post)) |
264
|
|
|
{ |
265
|
|
|
$search_params['types'] = $this->_req->post->types; |
266
|
|
|
foreach (array_keys($params) as $param_name) |
267
|
|
|
{ |
268
|
|
|
if (isset($this->_req->post->{$param_name})) |
269
|
|
|
{ |
270
|
|
|
$search_params[$param_name] = $this->_req->post->{$param_name}; |
271
|
|
|
} |
272
|
|
|
} |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
$search_url_params = isset($search_params) ? base64_encode(json_encode($search_params)) : null; |
276
|
|
|
|
277
|
|
|
// @todo Validate a little more. |
278
|
|
|
// Loop through every field of the form. |
279
|
|
|
$query_parts = array(); |
280
|
|
|
$where_params = array(); |
281
|
|
|
foreach ($params as $param_name => $param_info) |
282
|
|
|
{ |
283
|
|
|
// Not filled in? |
284
|
|
|
if (!isset($search_params[$param_name]) || $search_params[$param_name] === '') |
285
|
|
|
{ |
286
|
|
|
continue; |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
// Make sure numeric values are really numeric. |
290
|
|
|
if (in_array($param_info['type'], array('int', 'age'))) |
291
|
|
|
{ |
292
|
|
|
$search_params[$param_name] = (int) $search_params[$param_name]; |
293
|
|
|
} |
294
|
|
|
// Date values have to match the specified format. |
295
|
|
|
elseif ($param_info['type'] === 'date') |
296
|
|
|
{ |
297
|
|
|
// Check if this date format is valid. |
298
|
|
|
if (preg_match('/^\d{4}-\d{1,2}-\d{1,2}$/', $search_params[$param_name]) == 0) |
299
|
|
|
{ |
300
|
|
|
continue; |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
$search_params[$param_name] = strtotime($search_params[$param_name]); |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
// Those values that are in some kind of range (<, <=, =, >=, >). |
307
|
|
|
if (!empty($param_info['range'])) |
308
|
|
|
{ |
309
|
|
|
// Default to '=', just in case... |
310
|
|
|
if (empty($range_trans[$search_params['types'][$param_name]])) |
311
|
|
|
{ |
312
|
|
|
$search_params['types'][$param_name] = '='; |
313
|
|
|
} |
314
|
|
|
|
315
|
|
|
// Handle special case 'age'. |
316
|
|
|
if ($param_info['type'] === 'age') |
317
|
|
|
{ |
318
|
|
|
// All people that were born between $lowerlimit and $upperlimit are currently the specified age. |
319
|
|
|
$datearray = getdate(forum_time()); |
320
|
|
|
$upperlimit = sprintf('%04d-%02d-%02d', $datearray['year'] - $search_params[$param_name], $datearray['mon'], $datearray['mday']); |
321
|
|
|
$lowerlimit = sprintf('%04d-%02d-%02d', $datearray['year'] - $search_params[$param_name] - 1, $datearray['mon'], $datearray['mday']); |
322
|
|
|
|
323
|
|
|
if (in_array($search_params['types'][$param_name], array('-', '--', '='))) |
324
|
|
|
{ |
325
|
|
|
$query_parts[] = ($param_info['db_fields'][0]) . ' > {string:' . $param_name . '_minlimit}'; |
326
|
|
|
$where_params[$param_name . '_minlimit'] = ($search_params['types'][$param_name] === '--' ? $upperlimit : $lowerlimit); |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
if (in_array($search_params['types'][$param_name], array('+', '++', '='))) |
330
|
|
|
{ |
331
|
|
|
$query_parts[] = ($param_info['db_fields'][0]) . ' <= {string:' . $param_name . '_pluslimit}'; |
332
|
|
|
$where_params[$param_name . '_pluslimit'] = ($search_params['types'][$param_name] === '++' ? $lowerlimit : $upperlimit); |
333
|
|
|
|
334
|
|
|
// Make sure that members that didn't set their birth year are not queried. |
335
|
|
|
$query_parts[] = ($param_info['db_fields'][0]) . ' > {date:dec_zero_date}'; |
336
|
|
|
$where_params['dec_zero_date'] = '0004-12-31'; |
337
|
|
|
} |
338
|
|
|
} |
339
|
|
|
// Special case - equals a date. |
340
|
|
|
elseif ($param_info['type'] === 'date' && $search_params['types'][$param_name] === '=') |
341
|
|
|
{ |
342
|
|
|
$query_parts[] = $param_info['db_fields'][0] . ' > ' . $search_params[$param_name] . ' AND ' . $param_info['db_fields'][0] . ' < ' . ($search_params[$param_name] + 86400); |
343
|
|
|
} |
344
|
|
|
else |
345
|
|
|
{ |
346
|
|
|
$query_parts[] = $param_info['db_fields'][0] . ' ' . $range_trans[$search_params['types'][$param_name]] . ' ' . $search_params[$param_name]; |
347
|
|
|
} |
348
|
|
|
} |
349
|
|
|
// Checkboxes. |
350
|
|
|
elseif ($param_info['type'] === 'checkbox') |
351
|
|
|
{ |
352
|
|
|
// Each checkbox or no checkbox at all is checked -> ignore. |
353
|
|
|
if (!is_array($search_params[$param_name]) || $search_params[$param_name] === [] || count($search_params[$param_name]) === count($param_info['values'])) |
354
|
|
|
{ |
355
|
|
|
continue; |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
$query_parts[] = ($param_info['db_fields'][0]) . ' IN ({array_string:' . $param_name . '_check})'; |
359
|
|
|
$where_params[$param_name . '_check'] = $search_params[$param_name]; |
360
|
|
|
} |
361
|
|
|
else |
362
|
|
|
{ |
363
|
|
|
// Replace the wildcard characters ('*' and '?') into MySQL ones. |
364
|
|
|
$parameter = strtolower(strtr(Util::htmlspecialchars($search_params[$param_name], ENT_QUOTES), array('%' => '\%', '_' => '\_', '*' => '%', '?' => '_'))); |
365
|
|
|
|
366
|
|
|
$query_parts[] = '({column_case_insensitive:' . implode('} LIKE {string_case_insensitive:' . $param_name . '_normal} OR {column_case_insensitive:', $param_info['db_fields']) . '} LIKE {string_case_insensitive:' . $param_name . '_normal})'; |
367
|
|
|
|
368
|
|
|
$where_params[$param_name . '_normal'] = '%' . $parameter . '%'; |
369
|
|
|
} |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
// Set up the membergroup query part. |
373
|
|
|
$mg_query_parts = array(); |
374
|
|
|
|
375
|
|
|
// Primary membergroups, but only if at least was not selected. |
376
|
|
|
if (!empty($search_params['membergroups'][1]) && count($context['membergroups']) !== count($search_params['membergroups'][1])) |
377
|
|
|
{ |
378
|
|
|
$mg_query_parts[] = 'mem.id_group IN ({array_int:group_check})'; |
379
|
|
|
$where_params['group_check'] = $search_params['membergroups'][1]; |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
// Additional membergroups (these are only relevant if not all primary groups where selected!). |
383
|
|
|
if (!empty($search_params['membergroups'][2]) && (empty($search_params['membergroups'][1]) || count($context['membergroups']) !== count($search_params['membergroups'][1]))) |
384
|
|
|
{ |
385
|
|
|
foreach ($search_params['membergroups'][2] as $mg) |
386
|
|
|
{ |
387
|
|
|
$mg_query_parts[] = 'FIND_IN_SET({int:add_group_' . $mg . '}, mem.additional_groups) != 0'; |
388
|
|
|
$where_params['add_group_' . $mg] = $mg; |
389
|
|
|
} |
390
|
|
|
} |
391
|
|
|
|
392
|
|
|
// Combine the one or two membergroup parts into one query part linked with an OR. |
393
|
|
|
if ($mg_query_parts !== []) |
394
|
|
|
{ |
395
|
|
|
$query_parts[] = '(' . implode(' OR ', $mg_query_parts) . ')'; |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
// Get all selected post count related membergroups. |
399
|
|
|
if (!empty($search_params['postgroups']) && count($search_params['postgroups']) !== count($context['postgroups'])) |
400
|
|
|
{ |
401
|
|
|
$query_parts[] = 'id_post_group IN ({array_int:post_groups})'; |
402
|
|
|
$where_params['post_groups'] = $search_params['postgroups']; |
403
|
|
|
} |
404
|
|
|
|
405
|
|
|
// Construct the where part of the query. |
406
|
|
|
$where = $query_parts === [] ? '1=1' : implode(' |
407
|
|
|
AND ', $query_parts); |
408
|
|
|
} |
409
|
|
|
else |
410
|
|
|
{ |
411
|
|
|
$search_url_params = null; |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
// Construct the additional URL part with the query info in it. |
415
|
|
|
$context['params_url'] = $context['sub_action'] === 'query' ? ['sa' => 'query', 'params' => $search_url_params] : []; |
416
|
|
|
|
417
|
|
|
// Get the title and sub template ready.. |
418
|
|
|
$context['page_title'] = $txt['admin_members']; |
419
|
|
|
$where_params = $where_params ?? []; |
420
|
|
|
|
421
|
|
|
$listOptions = array( |
422
|
|
|
'id' => 'member_list', |
423
|
|
|
'title' => $txt['members_list'], |
424
|
|
|
'items_per_page' => $modSettings['defaultMaxMembers'], |
425
|
|
|
'base_href' => getUrl('admin', ['action' => 'admin', 'area' => 'viewmembers'] + $context['params_url']), |
426
|
|
|
'default_sort_col' => 'user_name', |
427
|
|
|
'get_items' => array( |
428
|
|
|
'file' => SUBSDIR . '/Members.subs.php', |
429
|
|
|
'function' => 'list_getMembers', |
430
|
|
|
'params' => array( |
431
|
|
|
$where ?? '1=1', |
432
|
|
|
$where_params, |
433
|
|
|
), |
434
|
|
|
), |
435
|
|
|
'get_count' => array( |
436
|
|
|
'file' => SUBSDIR . '/Members.subs.php', |
437
|
|
|
'function' => 'list_getNumMembers', |
438
|
|
|
'params' => array( |
439
|
|
|
$where ?? '1=1', |
440
|
|
|
$where_params, |
441
|
|
|
), |
442
|
|
|
), |
443
|
|
|
'columns' => array( |
444
|
|
|
'id_member' => array( |
445
|
|
|
'header' => array( |
446
|
|
|
'value' => $txt['member_id'], |
447
|
|
|
), |
448
|
|
|
'data' => array( |
449
|
|
|
'db' => 'id_member', |
450
|
|
|
), |
451
|
|
|
'sort' => array( |
452
|
|
|
'default' => 'id_member', |
453
|
|
|
'reverse' => 'id_member DESC', |
454
|
|
|
), |
455
|
|
|
), |
456
|
|
|
'user_name' => array( |
457
|
|
|
'header' => array( |
458
|
|
|
'value' => $txt['username'], |
459
|
|
|
), |
460
|
|
|
'data' => array( |
461
|
|
|
'sprintf' => array( |
462
|
|
|
'format' => '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => '%1$d', 'name' => '%2$s']) . '">%2$s</a>', |
463
|
|
|
'params' => array( |
464
|
|
|
'id_member' => false, |
465
|
|
|
'member_name' => false, |
466
|
|
|
), |
467
|
|
|
), |
468
|
|
|
), |
469
|
|
|
'sort' => array( |
470
|
|
|
'default' => 'member_name', |
471
|
|
|
'reverse' => 'member_name DESC', |
472
|
|
|
), |
473
|
|
|
), |
474
|
|
|
'display_name' => array( |
475
|
|
|
'header' => array( |
476
|
|
|
'value' => $txt['display_name'], |
477
|
|
|
), |
478
|
|
|
'data' => array( |
479
|
|
|
'sprintf' => array( |
480
|
|
|
'format' => '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => '%1$d']) . '">%2$s</a>', |
481
|
|
|
'params' => array( |
482
|
|
|
'id_member' => false, |
483
|
|
|
'real_name' => false, |
484
|
|
|
), |
485
|
|
|
), |
486
|
|
|
), |
487
|
|
|
'sort' => array( |
488
|
|
|
'default' => 'real_name', |
489
|
|
|
'reverse' => 'real_name DESC', |
490
|
|
|
), |
491
|
|
|
), |
492
|
|
|
'email' => array( |
493
|
|
|
'header' => array( |
494
|
|
|
'value' => $txt['email_address'], |
495
|
|
|
), |
496
|
|
|
'data' => array( |
497
|
|
|
'sprintf' => array( |
498
|
|
|
'format' => '<a href="mailto:%1$s">%1$s</a>', |
499
|
|
|
'params' => array( |
500
|
|
|
'email_address' => true, |
501
|
|
|
), |
502
|
|
|
), |
503
|
|
|
), |
504
|
|
|
'sort' => array( |
505
|
|
|
'default' => 'email_address', |
506
|
|
|
'reverse' => 'email_address DESC', |
507
|
|
|
), |
508
|
|
|
), |
509
|
|
|
'ip' => array( |
510
|
|
|
'header' => array( |
511
|
|
|
'value' => $txt['ip_address'], |
512
|
|
|
), |
513
|
|
|
'data' => array( |
514
|
|
|
'sprintf' => array( |
515
|
|
|
'format' => '<a href="' . getUrl('action', ['action' => 'trackip', 'searchip' => '%1$s']) . '">%1$s</a>', |
516
|
|
|
'params' => array( |
517
|
|
|
'member_ip' => false, |
518
|
|
|
), |
519
|
|
|
), |
520
|
|
|
), |
521
|
|
|
'sort' => array( |
522
|
|
|
'default' => 'member_ip', |
523
|
|
|
'reverse' => 'member_ip DESC', |
524
|
|
|
), |
525
|
|
|
), |
526
|
|
|
'last_active' => array( |
527
|
|
|
'header' => array( |
528
|
|
|
'value' => $txt['viewmembers_online'], |
529
|
|
|
), |
530
|
|
|
'data' => array( |
531
|
|
|
'function' => static function ($rowData) { |
532
|
|
|
global $txt; |
533
|
|
|
|
534
|
|
|
require_once(SUBSDIR . '/Members.subs.php'); |
535
|
|
|
|
536
|
|
|
// Calculate number of days since last online. |
537
|
|
|
$difference = empty($rowData['last_login']) ? $txt['never'] : htmlTime($rowData['last_login']); |
538
|
|
|
|
539
|
|
|
// Show it in italics if they're not activated... |
540
|
|
|
if ($rowData['is_activated'] % 10 !== 1) |
541
|
|
|
{ |
542
|
|
|
return sprintf('<em title="%1$s">%2$s</em>', $txt['not_activated'], $difference); |
543
|
|
|
} |
544
|
|
|
|
545
|
|
|
return $difference; |
546
|
|
|
}, |
547
|
|
|
), |
548
|
|
|
'sort' => array( |
549
|
|
|
'default' => 'last_login DESC', |
550
|
|
|
'reverse' => 'last_login', |
551
|
|
|
), |
552
|
|
|
), |
553
|
|
|
'posts' => array( |
554
|
|
|
'header' => array( |
555
|
|
|
'value' => $txt['member_postcount'], |
556
|
|
|
), |
557
|
|
|
'data' => array( |
558
|
|
|
'db' => 'posts', |
559
|
|
|
), |
560
|
|
|
'sort' => array( |
561
|
|
|
'default' => 'posts', |
562
|
|
|
'reverse' => 'posts DESC', |
563
|
|
|
), |
564
|
|
|
), |
565
|
|
|
'check' => array( |
566
|
|
|
'header' => array( |
567
|
|
|
'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" class="input_check" />', |
568
|
|
|
'class' => 'centertext', |
569
|
|
|
), |
570
|
|
|
'data' => array( |
571
|
|
|
'function' => static fn($rowData) => '<input type="checkbox" name="members[]" value="' . $rowData['id_member'] . '" class="input_check" ' . ($rowData['id_member'] === User::$info->id || $rowData['id_group'] == 1 || in_array(1, explode(',', $rowData['additional_groups'])) ? 'disabled="disabled"' : '') . ' />', |
|
|
|
|
572
|
|
|
'class' => 'centertext', |
573
|
|
|
), |
574
|
|
|
), |
575
|
|
|
), |
576
|
|
|
'form' => array( |
577
|
|
|
'href' => getUrl('admin', ['action' => 'admin', 'area' => 'viewmembers'] + $context['params_url']), |
578
|
|
|
'include_start' => true, |
579
|
|
|
'include_sort' => true, |
580
|
|
|
), |
581
|
|
|
'additional_rows' => array( |
582
|
|
|
array( |
583
|
|
|
'position' => 'below_table_data', |
584
|
|
|
'value' => template_users_multiactions($this->_getGroups()), |
585
|
|
|
'class' => 'flow_flex_additional_row', |
586
|
|
|
), |
587
|
|
|
), |
588
|
|
|
); |
589
|
|
|
|
590
|
|
|
// Without enough permissions, don't show 'delete members' checkboxes. |
591
|
|
|
if (!allowedTo('profile_remove_any')) |
592
|
|
|
{ |
593
|
|
|
unset($listOptions['cols']['check'], $listOptions['form'], $listOptions['additional_rows']); |
594
|
|
|
} |
595
|
|
|
|
596
|
|
|
createList($listOptions); |
597
|
|
|
|
598
|
|
|
$context['sub_template'] = 'show_list'; |
599
|
|
|
$context['default_list'] = 'member_list'; |
600
|
|
|
} |
601
|
|
|
|
602
|
|
|
/** |
603
|
|
|
* Handle mass action processing on a group of members |
604
|
|
|
* |
605
|
|
|
* - Deleting members |
606
|
|
|
* - Group changes |
607
|
|
|
* - Banning |
608
|
|
|
*/ |
609
|
|
|
protected function _multiMembersAction() |
610
|
|
|
{ |
611
|
|
|
global $txt; |
612
|
|
|
|
613
|
|
|
// @todo add a token too? |
614
|
|
|
checkSession(); |
615
|
|
|
|
616
|
|
|
// Clean the input. |
617
|
|
|
$members = array(); |
618
|
|
|
foreach ($this->_req->post->members as $value) |
619
|
|
|
{ |
620
|
|
|
// Don't delete yourself, idiot. |
621
|
|
|
if ($this->_req->post->maction === 'delete' && $value == $this->user->id) |
622
|
|
|
{ |
623
|
|
|
continue; |
624
|
|
|
} |
625
|
|
|
|
626
|
|
|
$members[] = (int) $value; |
627
|
|
|
} |
628
|
|
|
|
629
|
|
|
$members = array_filter($members); |
630
|
|
|
|
631
|
|
|
// No members, nothing to do. |
632
|
|
|
if (empty($members)) |
633
|
|
|
{ |
634
|
|
|
return; |
635
|
|
|
} |
636
|
|
|
|
637
|
|
|
// Are we performing a delete? |
638
|
|
|
if ($this->_req->post->maction === 'delete' && allowedTo('profile_remove_any')) |
639
|
|
|
{ |
640
|
|
|
// Delete all the selected members. |
641
|
|
|
require_once(SUBSDIR . '/Members.subs.php'); |
642
|
|
|
deleteMembers($members, true); |
643
|
|
|
} |
644
|
|
|
|
645
|
|
|
// Are we changing groups? |
646
|
|
|
if (in_array($this->_req->post->maction, array('pgroup', 'agroup')) && allowedTo('manage_membergroups')) |
647
|
|
|
{ |
648
|
|
|
require_once(SUBSDIR . '/Membergroups.subs.php'); |
649
|
|
|
|
650
|
|
|
$groups = array('p', 'a'); |
651
|
|
|
foreach ($groups as $group) |
652
|
|
|
{ |
653
|
|
|
if ($this->_req->post->maction == $group . 'group' && !empty($this->_req->post->new_membergroup)) |
654
|
|
|
{ |
655
|
|
|
$type = $group === 'p' ? 'force_primary' : 'only_additional'; |
656
|
|
|
|
657
|
|
|
// Change all the selected members' group. |
658
|
|
|
if ($this->_req->post->new_membergroup != -1) |
659
|
|
|
{ |
660
|
|
|
addMembersToGroup($members, $this->_req->post->new_membergroup, $type, true); |
661
|
|
|
} |
662
|
|
|
else |
663
|
|
|
{ |
664
|
|
|
removeMembersFromGroups($members, null, true); |
665
|
|
|
} |
666
|
|
|
} |
667
|
|
|
} |
668
|
|
|
} |
669
|
|
|
|
670
|
|
|
// Are we banning? |
671
|
|
|
if (in_array($this->_req->post->maction, array('ban_names', 'ban_mails', 'ban_ips', 'ban_names_mails')) && allowedTo('manage_bans')) |
672
|
|
|
{ |
673
|
|
|
require_once(SUBSDIR . '/Bans.subs.php'); |
674
|
|
|
require_once(SUBSDIR . '/Members.subs.php'); |
675
|
|
|
|
676
|
|
|
$ban_group_id = insertBanGroup(array( |
677
|
|
|
'name' => $txt['admin_ban_name'], |
678
|
|
|
'cannot' => array( |
679
|
|
|
'access' => 1, |
680
|
|
|
'register' => 0, |
681
|
|
|
'post' => 0, |
682
|
|
|
'login' => 0, |
683
|
|
|
), |
684
|
|
|
'db_expiration' => 'NULL', |
685
|
|
|
'reason' => '', |
686
|
|
|
'notes' => '', |
687
|
|
|
)); |
688
|
|
|
|
689
|
|
|
$ban_name = in_array($this->_req->post->maction, array('ban_names', 'ban_names_mails')); |
690
|
|
|
$ban_email = in_array($this->_req->post->maction, array('ban_mails', 'ban_names_mails')); |
691
|
|
|
$ban_ips = $this->_req->post->maction === 'ban_ips'; |
692
|
|
|
$suggestions = array(); |
693
|
|
|
|
694
|
|
|
if ($ban_email) |
695
|
|
|
{ |
696
|
|
|
$suggestions[] = 'email'; |
697
|
|
|
} |
698
|
|
|
|
699
|
|
|
if ($ban_name) |
700
|
|
|
{ |
701
|
|
|
$suggestions[] = 'user'; |
702
|
|
|
} |
703
|
|
|
|
704
|
|
|
if ($ban_ips) |
705
|
|
|
{ |
706
|
|
|
$suggestions[] = 'main_ip'; |
707
|
|
|
} |
708
|
|
|
|
709
|
|
|
$members_data = getBasicMemberData($members, array('moderation' => true)); |
710
|
|
|
foreach ($members_data as $member) |
711
|
|
|
{ |
712
|
|
|
saveTriggers(array( |
713
|
|
|
'main_ip' => $ban_ips ? $member['member_ip'] : '', |
714
|
|
|
'hostname' => '', |
715
|
|
|
'email' => $ban_email ? $member['email_address'] : '', |
716
|
|
|
'user' => $ban_name ? $member['member_name'] : '', |
717
|
|
|
'ban_suggestions' => $suggestions, |
718
|
|
|
), $ban_group_id, $ban_name ? $member['id_member'] : 0); |
719
|
|
|
} |
720
|
|
|
} |
721
|
|
|
} |
722
|
|
|
|
723
|
|
|
/** |
724
|
|
|
* Prepares the list of groups to be used in the dropdown for "mass actions". |
725
|
|
|
* |
726
|
|
|
* @return array |
727
|
|
|
*/ |
728
|
|
|
protected function _getGroups() |
729
|
|
|
{ |
730
|
|
|
global $txt; |
731
|
|
|
|
732
|
|
|
require_once(SUBSDIR . '/Membergroups.subs.php'); |
733
|
|
|
|
734
|
|
|
$member_groups = getGroupsList(); |
735
|
|
|
|
736
|
|
|
// Better remove admin membergroup...and set it to a "remove all" |
737
|
|
|
$member_groups[1] = array( |
738
|
|
|
'id' => -1, |
739
|
|
|
'name' => $txt['remove_groups'], |
740
|
|
|
'is_primary' => 0, |
741
|
|
|
); |
742
|
|
|
|
743
|
|
|
// no primary is tricky... |
744
|
|
|
$member_groups[0] = array( |
745
|
|
|
'id' => 0, |
746
|
|
|
'name' => '', |
747
|
|
|
'is_primary' => 1, |
748
|
|
|
); |
749
|
|
|
|
750
|
|
|
return $member_groups; |
751
|
|
|
} |
752
|
|
|
|
753
|
|
|
/** |
754
|
|
|
* Search the member list, using one or more criteria. |
755
|
|
|
* |
756
|
|
|
* What it does: |
757
|
|
|
* |
758
|
|
|
* - Called by ?action=admin;area=viewmembers;sa=search. |
759
|
|
|
* - Requires the moderate_forum permission. |
760
|
|
|
* - form is submitted to action=admin;area=viewmembers;sa=query. |
761
|
|
|
* |
762
|
|
|
* @uses the search_members sub template of the ManageMembers template. |
763
|
|
|
*/ |
764
|
|
|
public function action_search() |
765
|
|
|
{ |
766
|
|
|
global $context, $txt; |
767
|
|
|
|
768
|
|
|
// Get a list of all the membergroups and postgroups that can be selected. |
769
|
|
|
require_once(SUBSDIR . '/Membergroups.subs.php'); |
770
|
|
|
$groups = getBasicMembergroupData(array(), array('moderator'), null, true); |
771
|
|
|
|
772
|
|
|
$context['membergroups'] = $groups['membergroups']; |
773
|
|
|
$context['postgroups'] = $groups['postgroups']; |
774
|
|
|
$context['page_title'] = $txt['admin_members']; |
775
|
|
|
$context['sub_template'] = 'search_members'; |
776
|
|
|
|
777
|
|
|
unset($groups); |
778
|
|
|
} |
779
|
|
|
|
780
|
|
|
/** |
781
|
|
|
* List all members who are awaiting approval / activation, sortable on different columns. |
782
|
|
|
* |
783
|
|
|
* What it does: |
784
|
|
|
* |
785
|
|
|
* - It allows instant approval or activation of (a selection of) members. |
786
|
|
|
* - Called by ?action=admin;area=viewmembers;sa=browse;type=approve |
787
|
|
|
* or ?action=admin;area=viewmembers;sa=browse;type=activate. |
788
|
|
|
* - The form submits to ?action=admin;area=viewmembers;sa=approve. |
789
|
|
|
* - Requires the moderate_forum permission. |
790
|
|
|
* |
791
|
|
|
* @event integrate_list_approve_list |
792
|
|
|
* @uses the admin_browse sub template of the ManageMembers template. |
793
|
|
|
*/ |
794
|
|
|
public function action_browse() |
795
|
|
|
{ |
796
|
|
|
global $txt, $context, $modSettings; |
797
|
|
|
|
798
|
|
|
// Not a lot here! |
799
|
|
|
$context['page_title'] = $txt['admin_members']; |
800
|
|
|
$context['sub_template'] = 'admin_browse'; |
801
|
|
|
$context['browse_type'] = $this->_req->query->type ?? (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 1 ? 'activate' : 'approve'); |
802
|
|
|
|
803
|
|
|
if (isset($context['tabs'][$context['browse_type']])) |
804
|
|
|
{ |
805
|
|
|
$context['tabs'][$context['browse_type']]['is_selected'] = true; |
806
|
|
|
} |
807
|
|
|
|
808
|
|
|
// Allowed filters are those we can have, in theory. |
809
|
|
|
$context['allowed_filters'] = $context['browse_type'] === 'approve' ? array(3, 4, 5) : array(0, 2); |
810
|
|
|
$context['current_filter'] = isset($this->_req->query->filter) && in_array($this->_req->query->filter, $context['allowed_filters']) && !empty($context['activation_numbers'][$this->_req->query->filter]) ? (int) $this->_req->query->filter : -1; |
811
|
|
|
|
812
|
|
|
// Sort out the different sub areas that we can actually filter by. |
813
|
|
|
$context['available_filters'] = array(); |
814
|
|
|
foreach ($context['activation_numbers'] as $type => $amount) |
815
|
|
|
{ |
816
|
|
|
// We have some of these... |
817
|
|
|
if (!in_array($type, $context['allowed_filters'])) |
818
|
|
|
{ |
819
|
|
|
continue; |
820
|
|
|
} |
821
|
|
|
|
822
|
|
|
if ($amount <= 0) |
823
|
|
|
{ |
824
|
|
|
continue; |
825
|
|
|
} |
826
|
|
|
|
827
|
|
|
$context['available_filters'][] = array( |
828
|
|
|
'type' => $type, |
829
|
|
|
'amount' => $amount, |
830
|
|
|
'desc' => $txt['admin_browse_filter_type_' . $type] ?? '?', |
831
|
|
|
'selected' => $type === $context['current_filter'] |
832
|
|
|
); |
833
|
|
|
} |
834
|
|
|
|
835
|
|
|
// If the filter was not sent, set it to whatever has people in it! |
836
|
|
|
if ($context['current_filter'] == -1 && !empty($context['available_filters'][0]['amount'])) |
837
|
|
|
{ |
838
|
|
|
$context['current_filter'] = $context['available_filters'][0]['type']; |
839
|
|
|
$context['available_filters'][0]['selected'] = true; |
840
|
|
|
} |
841
|
|
|
|
842
|
|
|
// This little variable is used to determine if we should flag where we are looking. |
843
|
|
|
$context['show_filter'] = ($context['current_filter'] != 0 && $context['current_filter'] != 3) || count($context['available_filters']) > 1; |
844
|
|
|
|
845
|
|
|
// The columns that can be sorted. |
846
|
|
|
$context['columns'] = array( |
847
|
|
|
'id_member' => array('label' => $txt['admin_browse_id']), |
848
|
|
|
'member_name' => array('label' => $txt['admin_browse_username']), |
849
|
|
|
'email_address' => array('label' => $txt['admin_browse_email']), |
850
|
|
|
'member_ip' => array('label' => $txt['admin_browse_ip']), |
851
|
|
|
'date_registered' => array('label' => $txt['admin_browse_registered']), |
852
|
|
|
); |
853
|
|
|
|
854
|
|
|
// Are we showing duplicate information? |
855
|
|
|
if (isset($this->_req->query->showdupes)) |
856
|
|
|
{ |
857
|
|
|
$_SESSION['showdupes'] = (int) $this->_req->query->showdupes; |
858
|
|
|
} |
859
|
|
|
|
860
|
|
|
$context['show_duplicates'] = !empty($_SESSION['showdupes']); |
861
|
|
|
|
862
|
|
|
// Determine which actions we should allow on this page. |
863
|
|
|
$context['allowed_actions'] = []; |
864
|
|
|
if ($context['browse_type'] === 'approve') |
865
|
|
|
{ |
866
|
|
|
// If we are approving deleted accounts we have a slightly different list... actually a mirror ;) |
867
|
|
|
if ($context['current_filter'] == 4) |
868
|
|
|
{ |
869
|
|
|
$context['allowed_actions'] = [ |
870
|
|
|
'reject' => $txt['admin_browse_w_approve_deletion'], |
871
|
|
|
'ok' => $txt['admin_browse_w_reject_delete'], |
872
|
|
|
]; |
873
|
|
|
} |
874
|
|
|
else |
875
|
|
|
{ |
876
|
|
|
$context['allowed_actions'] = [ |
877
|
|
|
'ok' => $txt['admin_browse_w_approve'], |
878
|
|
|
'okemail' => $txt['admin_browse_w_approve'] . ' ' . $txt['admin_browse_w_email'], |
879
|
|
|
'require_activation' => $txt['admin_browse_w_approve_require_activate'], |
880
|
|
|
'reject' => $txt['admin_browse_w_reject'], |
881
|
|
|
'rejectemail' => $txt['admin_browse_w_reject'] . ' ' . $txt['admin_browse_w_email'], |
882
|
|
|
]; |
883
|
|
|
} |
884
|
|
|
} |
885
|
|
|
elseif ($context['browse_type'] === 'activate') |
886
|
|
|
{ |
887
|
|
|
$context['allowed_actions'] = [ |
888
|
|
|
'ok' => $txt['admin_browse_w_activate'], |
889
|
|
|
'okemail' => $txt['admin_browse_w_activate'] . ' ' . $txt['admin_browse_w_email'], |
890
|
|
|
'delete' => $txt['admin_browse_w_delete'], |
891
|
|
|
'deleteemail' => $txt['admin_browse_w_delete'] . ' ' . $txt['admin_browse_w_email'], |
892
|
|
|
'remind' => $txt['admin_browse_w_remind'] . ' ' . $txt['admin_browse_w_email'], |
893
|
|
|
]; |
894
|
|
|
} |
895
|
|
|
|
896
|
|
|
// Create an option list for actions allowed to be done with selected members. |
897
|
|
|
$allowed_actions = ' |
898
|
|
|
<option selected="selected" value="">' . $txt['admin_browse_with_selected'] . ':</option> |
899
|
|
|
<option value="" disabled="disabled">' . str_repeat('—', strlen($txt['admin_browse_with_selected'])) . '</option>'; |
900
|
|
|
|
901
|
|
|
foreach ($context['allowed_actions'] as $key => $desc) |
902
|
|
|
{ |
903
|
|
|
$allowed_actions .= ' |
904
|
|
|
<option value="' . $key . '">' . '➤ ' . $desc . '</option>'; |
905
|
|
|
} |
906
|
|
|
|
907
|
|
|
// Setup the Javascript function for selecting an action for the list. |
908
|
|
|
$javascript = ' |
909
|
|
|
function onSelectChange() |
910
|
|
|
{ |
911
|
|
|
if (document.forms.postForm.todo.value === "") |
912
|
|
|
return; |
913
|
|
|
|
914
|
|
|
var message = "";'; |
915
|
|
|
|
916
|
|
|
// We have special messages for approving deletion of accounts - it's surprisingly logical - honest. |
917
|
|
|
if ($context['current_filter'] == 4) |
918
|
|
|
{ |
919
|
|
|
$javascript .= ' |
920
|
|
|
if (document.forms.postForm.todo.value.indexOf("reject") !== -1) |
921
|
|
|
message = "' . $txt['admin_browse_w_delete'] . '"; |
922
|
|
|
else |
923
|
|
|
message = "' . $txt['admin_browse_w_reject'] . '";'; |
924
|
|
|
} |
925
|
|
|
// Otherwise a nice standard message. |
926
|
|
|
else |
927
|
|
|
{ |
928
|
|
|
$javascript .= ' |
929
|
|
|
if (document.forms.postForm.todo.value.indexOf("delete") !== -1) |
930
|
|
|
message = "' . $txt['admin_browse_w_delete'] . '"; |
931
|
|
|
else if (document.forms.postForm.todo.value.indexOf("reject") !== -1) |
932
|
|
|
message = "' . $txt['admin_browse_w_reject'] . '"; |
933
|
|
|
else if (document.forms.postForm.todo.value == "remind") |
934
|
|
|
message = "' . $txt['admin_browse_w_remind'] . '"; |
935
|
|
|
else |
936
|
|
|
message = "' . ($context['browse_type'] === 'approve' ? $txt['admin_browse_w_approve'] : $txt['admin_browse_w_activate']) . '";'; |
937
|
|
|
} |
938
|
|
|
|
939
|
|
|
$javascript .= ' |
940
|
|
|
if (confirm(message + " ' . $txt['admin_browse_warn'] . '")) |
941
|
|
|
document.forms.postForm.submit(); |
942
|
|
|
}'; |
943
|
|
|
|
944
|
|
|
$listOptions = array( |
945
|
|
|
'id' => 'approve_list', |
946
|
|
|
'items_per_page' => $modSettings['defaultMaxMembers'], |
947
|
|
|
'base_href' => getUrl('admin', ['action' => 'admin', 'area' => 'viewmembers', 'sa' => 'browse', 'type' => $context['browse_type'] . (empty($context['show_filter']) ? '' : ", 'filter' =>" . $context['current_filter'])]), |
948
|
|
|
'default_sort_col' => 'date_registered', |
949
|
|
|
'get_items' => array( |
950
|
|
|
'file' => SUBSDIR . '/Members.subs.php', |
951
|
|
|
'function' => 'list_getMembers', |
952
|
|
|
'params' => array( |
953
|
|
|
'is_activated = {int:activated_status}', |
954
|
|
|
array('activated_status' => $context['current_filter']), |
955
|
|
|
$context['show_duplicates'], |
956
|
|
|
), |
957
|
|
|
), |
958
|
|
|
'get_count' => array( |
959
|
|
|
'file' => SUBSDIR . '/Members.subs.php', |
960
|
|
|
'function' => 'list_getNumMembers', |
961
|
|
|
'params' => array( |
962
|
|
|
'is_activated = {int:activated_status}', |
963
|
|
|
array('activated_status' => $context['current_filter']), |
964
|
|
|
), |
965
|
|
|
), |
966
|
|
|
'columns' => array( |
967
|
|
|
'id_member' => array( |
968
|
|
|
'header' => array( |
969
|
|
|
'value' => $txt['member_id'], |
970
|
|
|
), |
971
|
|
|
'data' => array( |
972
|
|
|
'db' => 'id_member', |
973
|
|
|
), |
974
|
|
|
'sort' => array( |
975
|
|
|
'default' => 'id_member', |
976
|
|
|
'reverse' => 'id_member DESC', |
977
|
|
|
), |
978
|
|
|
), |
979
|
|
|
'user_name' => array( |
980
|
|
|
'header' => array( |
981
|
|
|
'value' => $txt['username'], |
982
|
|
|
), |
983
|
|
|
'data' => array( |
984
|
|
|
'sprintf' => array( |
985
|
|
|
'format' => '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => '%1$d']) . '">%2$s</a>', |
986
|
|
|
'params' => array( |
987
|
|
|
'id_member' => false, |
988
|
|
|
'member_name' => false, |
989
|
|
|
), |
990
|
|
|
), |
991
|
|
|
), |
992
|
|
|
'sort' => array( |
993
|
|
|
'default' => 'member_name', |
994
|
|
|
'reverse' => 'member_name DESC', |
995
|
|
|
), |
996
|
|
|
), |
997
|
|
|
'email' => array( |
998
|
|
|
'header' => array( |
999
|
|
|
'value' => $txt['email_address'], |
1000
|
|
|
), |
1001
|
|
|
'data' => array( |
1002
|
|
|
'sprintf' => array( |
1003
|
|
|
'format' => '<a href="mailto:%1$s">%1$s</a>', |
1004
|
|
|
'params' => array( |
1005
|
|
|
'email_address' => true, |
1006
|
|
|
), |
1007
|
|
|
), |
1008
|
|
|
), |
1009
|
|
|
'sort' => array( |
1010
|
|
|
'default' => 'email_address', |
1011
|
|
|
'reverse' => 'email_address DESC', |
1012
|
|
|
), |
1013
|
|
|
), |
1014
|
|
|
'ip' => array( |
1015
|
|
|
'header' => array( |
1016
|
|
|
'value' => $txt['ip_address'], |
1017
|
|
|
), |
1018
|
|
|
'data' => array( |
1019
|
|
|
'sprintf' => array( |
1020
|
|
|
'format' => '<a href="' . getUrl('profile', ['action' => 'trackip', 'searchip' => '%1$s']) . '">%1$s</a>', |
1021
|
|
|
'params' => array( |
1022
|
|
|
'member_ip' => false, |
1023
|
|
|
), |
1024
|
|
|
), |
1025
|
|
|
), |
1026
|
|
|
'sort' => array( |
1027
|
|
|
'default' => 'member_ip', |
1028
|
|
|
'reverse' => 'member_ip DESC', |
1029
|
|
|
), |
1030
|
|
|
), |
1031
|
|
|
'hostname' => array( |
1032
|
|
|
'header' => array( |
1033
|
|
|
'value' => $txt['hostname'], |
1034
|
|
|
), |
1035
|
|
|
'data' => array( |
1036
|
|
|
'function' => static fn($rowData) => host_from_ip($rowData['member_ip']), |
1037
|
|
|
'class' => 'smalltext', |
1038
|
|
|
), |
1039
|
|
|
), |
1040
|
|
|
'date_registered' => array( |
1041
|
|
|
'header' => array( |
1042
|
|
|
'value' => $context['current_filter'] == 4 ? $txt['viewmembers_online'] : $txt['date_registered'], |
1043
|
|
|
), |
1044
|
|
|
'data' => array( |
1045
|
|
|
'function' => static function ($rowData) { |
1046
|
|
|
global $context; |
1047
|
|
|
return standardTime($rowData[($context['current_filter'] == 4 ? 'last_login' : 'date_registered')]); |
1048
|
|
|
}, |
1049
|
|
|
), |
1050
|
|
|
'sort' => array( |
1051
|
|
|
'default' => $context['current_filter'] == 4 ? 'mem.last_login DESC' : 'date_registered DESC', |
1052
|
|
|
'reverse' => $context['current_filter'] == 4 ? 'mem.last_login' : 'date_registered', |
1053
|
|
|
), |
1054
|
|
|
), |
1055
|
|
|
'duplicates' => array( |
1056
|
|
|
'header' => array( |
1057
|
|
|
'value' => $txt['duplicates'], |
1058
|
|
|
// Make sure it doesn't go too wide. |
1059
|
|
|
'style' => 'width: 20%;', |
1060
|
|
|
), |
1061
|
|
|
'data' => array( |
1062
|
|
|
'function' => static function ($rowData) { |
1063
|
|
|
global $txt; |
1064
|
|
|
|
1065
|
|
|
$member_links = array(); |
1066
|
|
|
foreach ($rowData['duplicate_members'] as $member) |
1067
|
|
|
{ |
1068
|
|
|
if ($member['id']) |
1069
|
|
|
{ |
1070
|
|
|
$member_links[] = '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => $member['id'], 'name' => $member['name']]) . '" ' . (empty($member['is_banned']) ? '' : 'class="alert"') . '>' . $member['name'] . '</a>'; |
1071
|
|
|
} |
1072
|
|
|
else |
1073
|
|
|
{ |
1074
|
|
|
$member_links[] = $member['name'] . ' (' . $txt['guest'] . ')'; |
1075
|
|
|
} |
1076
|
|
|
} |
1077
|
|
|
|
1078
|
|
|
return implode(', ', $member_links); |
1079
|
|
|
}, |
1080
|
|
|
'class' => 'smalltext', |
1081
|
|
|
), |
1082
|
|
|
), |
1083
|
|
|
'check' => array( |
1084
|
|
|
'header' => array( |
1085
|
|
|
'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" class="input_check" />', |
1086
|
|
|
'class' => 'centertext', |
1087
|
|
|
), |
1088
|
|
|
'data' => array( |
1089
|
|
|
'sprintf' => array( |
1090
|
|
|
'format' => '<input type="checkbox" name="todoAction[]" value="%1$d" class="input_check" />', |
1091
|
|
|
'params' => array( |
1092
|
|
|
'id_member' => false, |
1093
|
|
|
), |
1094
|
|
|
), |
1095
|
|
|
'class' => 'centertext', |
1096
|
|
|
), |
1097
|
|
|
), |
1098
|
|
|
), |
1099
|
|
|
'javascript' => $javascript, |
1100
|
|
|
'form' => array( |
1101
|
|
|
'href' => getUrl('action', ['action' => 'admin', 'area' => 'viewmembers', 'sa' => 'approve', 'type' => $context['browse_type']]), |
1102
|
|
|
'name' => 'postForm', |
1103
|
|
|
'include_start' => true, |
1104
|
|
|
'include_sort' => true, |
1105
|
|
|
'hidden_fields' => array( |
1106
|
|
|
'orig_filter' => $context['current_filter'], |
1107
|
|
|
), |
1108
|
|
|
), |
1109
|
|
|
'additional_rows' => array( |
1110
|
|
|
array( |
1111
|
|
|
'position' => 'below_table_data', |
1112
|
|
|
'class' => 'flow_flex_additional_row', |
1113
|
|
|
'value' => ' |
1114
|
|
|
<div class="submitbutton"> |
1115
|
|
|
<a class="linkbutton" href="' . getUrl('action', ['action' => 'admin', 'area' => 'viewmembers', 'sa' => 'browse', 'showdupes' => $context['show_duplicates'] ? 0 : 1, 'type' => $context['browse_type'], '{session_data}'] + (empty($context['show_filter']) ? [] : ['filter' => $context['current_filter']])) . '">' . ($context['show_duplicates'] ? $txt['dont_check_for_duplicate'] : $txt['check_for_duplicate']) . '</a> |
1116
|
|
|
<select name="todo" onchange="onSelectChange();"> |
1117
|
|
|
' . $allowed_actions . ' |
1118
|
|
|
</select> |
1119
|
|
|
<noscript> |
1120
|
|
|
<input type="submit" value="' . $txt['go'] . '" /> |
1121
|
|
|
</noscript> |
1122
|
|
|
</div> |
1123
|
|
|
', |
1124
|
|
|
), |
1125
|
|
|
), |
1126
|
|
|
); |
1127
|
|
|
|
1128
|
|
|
// Pick what column to actually include if we're showing duplicates. |
1129
|
|
|
if ($context['show_duplicates']) |
1130
|
|
|
{ |
1131
|
|
|
unset($listOptions['columns']['email']); |
1132
|
|
|
} |
1133
|
|
|
else |
1134
|
|
|
{ |
1135
|
|
|
unset($listOptions['columns']['duplicates']); |
1136
|
|
|
} |
1137
|
|
|
|
1138
|
|
|
// Only show hostname on duplicates as it takes a lot of time. |
1139
|
|
|
if (!$context['show_duplicates'] || !empty($modSettings['disableHostnameLookup'])) |
1140
|
|
|
{ |
1141
|
|
|
unset($listOptions['columns']['hostname']); |
1142
|
|
|
} |
1143
|
|
|
|
1144
|
|
|
// Is there any need to show filters? |
1145
|
|
|
if (isset($context['available_filters'])) |
1146
|
|
|
{ |
1147
|
|
|
$listOptions['list_menu'] = array( |
1148
|
|
|
'show_on' => 'top', |
1149
|
|
|
'links' => array() |
1150
|
|
|
); |
1151
|
|
|
|
1152
|
|
|
foreach ($context['available_filters'] as $filter) |
1153
|
|
|
{ |
1154
|
|
|
$listOptions['list_menu']['links'][] = array( |
1155
|
|
|
'is_selected' => $filter['selected'], |
1156
|
|
|
'href' => getUrl('action', ['action' => 'admin', 'area' => 'viewmembers', 'sa' => 'browse', 'type' => $context['browse_type'], 'filter' => $filter['type']]), |
1157
|
|
|
'label' => $filter['desc'] . ' - ' . $filter['amount'] . ' ' . ($filter['amount'] == 1 ? $txt['user'] : $txt['users']) |
1158
|
|
|
); |
1159
|
|
|
} |
1160
|
|
|
} |
1161
|
|
|
|
1162
|
|
|
// Now that we have all the options, create the list. |
1163
|
|
|
createList($listOptions); |
1164
|
|
|
} |
1165
|
|
|
|
1166
|
|
|
/** |
1167
|
|
|
* This function handles the approval, rejection, activation or deletion of members. |
1168
|
|
|
* |
1169
|
|
|
* What it does: |
1170
|
|
|
* |
1171
|
|
|
* - Called by ?action=admin;area=viewmembers;sa=approve. |
1172
|
|
|
* - Requires the moderate_forum permission. |
1173
|
|
|
* - Redirects to ?action=admin;area=viewmembers;sa=browse |
1174
|
|
|
* with the same parameters as the calling page. |
1175
|
|
|
*/ |
1176
|
|
|
public function action_approve() |
1177
|
|
|
{ |
1178
|
|
|
global $modSettings; |
1179
|
|
|
|
1180
|
|
|
// First, check our session. |
1181
|
|
|
checkSession(); |
1182
|
|
|
|
1183
|
|
|
require_once(SUBSDIR . '/Mail.subs.php'); |
1184
|
|
|
require_once(SUBSDIR . '/Members.subs.php'); |
1185
|
|
|
|
1186
|
|
|
// We also need to the login languages here - for emails. |
1187
|
|
|
Txt::load('Login'); |
1188
|
|
|
|
1189
|
|
|
// Start off clean |
1190
|
|
|
$this->conditions = array(); |
1191
|
|
|
|
1192
|
|
|
// Sort out where we are going... |
1193
|
|
|
$original_filter = $this->_req->getPost('orig_filter', 'intval', null); |
1194
|
|
|
$current_filter = $this->conditions['activated_status'] = $original_filter; |
1195
|
|
|
|
1196
|
|
|
$type = $this->_req->getQuery('type', 'trim', ''); |
1197
|
|
|
$filter = $this->_req->getPost('filter', 'trim', null); |
1198
|
|
|
$sort = $this->_req->getRequest('sort', 'trim', ''); |
1199
|
|
|
$start = $this->_req->getRequest('start', 'intval', 0); |
1200
|
|
|
$todoAction = $this->_req->getPost('todoAction'); |
1201
|
|
|
$time_passed = $this->_req->getPost('$time_passed', 'intval'); |
1202
|
|
|
$todo = $this->_req->getPost('todo', 'trim'); |
1203
|
|
|
|
1204
|
|
|
// If we are applying a filter do just that - then redirect. |
1205
|
|
|
if (isset($filter) && $filter !== $original_filter) |
1206
|
|
|
{ |
1207
|
|
|
redirectexit('action=admin;area=viewmembers;sa=browse;type=' . $type . ';sort=' . $sort . ';filter=' . $filter . ';start=' . $start); |
1208
|
|
|
} |
1209
|
|
|
|
1210
|
|
|
// Nothing to do? |
1211
|
|
|
if (!isset($todoAction) && !isset($time_passed)) |
1212
|
|
|
{ |
1213
|
|
|
redirectexit('action=admin;area=viewmembers;sa=browse;type=' . $type . ';sort=' . $sort . ';filter=' . $current_filter . ';start=' . $start); |
1214
|
|
|
} |
1215
|
|
|
|
1216
|
|
|
// Are we dealing with members who have been waiting for > set amount of time? |
1217
|
|
|
if (isset($time_passed)) |
1218
|
|
|
{ |
1219
|
|
|
$this->conditions['time_before'] = time() - 86400 * $time_passed; |
1220
|
|
|
} |
1221
|
|
|
// Coming from checkboxes - validate the members passed through to us. |
1222
|
|
|
else |
1223
|
|
|
{ |
1224
|
|
|
$this->conditions['members'] = array(); |
1225
|
|
|
foreach ($todoAction as $id) |
1226
|
|
|
{ |
1227
|
|
|
$this->conditions['members'][] = (int) $id; |
1228
|
|
|
} |
1229
|
|
|
} |
1230
|
|
|
|
1231
|
|
|
$data = retrieveMemberData($this->conditions); |
1232
|
|
|
if ($data['member_count'] === 0) |
1233
|
|
|
{ |
1234
|
|
|
redirectexit('action=admin;area=viewmembers;sa=browse;type=' . $type . ';sort=' . $sort . ';filter=' . $current_filter . ';start=' . $start); |
1235
|
|
|
} |
1236
|
|
|
|
1237
|
|
|
$this->member_info = $data['member_info']; |
1238
|
|
|
$this->conditions['members'] = $data['members']; |
1239
|
|
|
|
1240
|
|
|
// What do we want to do with this application? |
1241
|
|
|
switch ($todo) |
1242
|
|
|
{ |
1243
|
|
|
// Are we activating or approving the members? |
1244
|
|
|
case 'ok': |
1245
|
|
|
case 'okemail': |
1246
|
|
|
$this->_okMember(); |
1247
|
|
|
break; |
1248
|
|
|
// Maybe we're sending it off for activation? |
1249
|
|
|
case 'require_activation': |
1250
|
|
|
$this->_requireMember(); |
1251
|
|
|
break; |
1252
|
|
|
// Are we rejecting them? |
1253
|
|
|
case 'reject': |
1254
|
|
|
case 'rejectemail': |
1255
|
|
|
$this->_rejectMember(); |
1256
|
|
|
break; |
1257
|
|
|
// A simple delete? |
1258
|
|
|
case 'delete': |
1259
|
|
|
case 'deleteemail': |
1260
|
|
|
$this->_deleteMember(); |
1261
|
|
|
break; |
1262
|
|
|
// Remind them to activate their account? |
1263
|
|
|
case 'remind': |
1264
|
|
|
$this->_remindMember(); |
1265
|
|
|
break; |
1266
|
|
|
} |
1267
|
|
|
|
1268
|
|
|
// Log what we did? Core features Moderation Logging must be enabled |
1269
|
|
|
if (featureEnabled('ml') && in_array($todo, ['ok', 'okemail', 'require_activation', 'remind'])) |
1270
|
|
|
{ |
1271
|
|
|
$log_action = $todo === 'remind' ? 'remind_member' : 'approve_member'; |
1272
|
|
|
|
1273
|
|
|
foreach ($this->member_info as $member) |
1274
|
|
|
{ |
1275
|
|
|
logAction($log_action, ['member' => $member['id']], 'admin'); |
1276
|
|
|
} |
1277
|
|
|
} |
1278
|
|
|
|
1279
|
|
|
// Although updateMemberStats *may* catch this, best to do it manually just in case (Doesn't always sort out unapprovedMembers). |
1280
|
|
|
if (in_array($current_filter, [3, 4, 5])) |
1281
|
|
|
{ |
1282
|
|
|
updateSettings(array('unapprovedMembers' => ($modSettings['unapprovedMembers'] > $data['member_count'] ? $modSettings['unapprovedMembers'] - $data['member_count'] : 0))); |
1283
|
|
|
} |
1284
|
|
|
|
1285
|
|
|
// Update the member's stats. (but, we know the member didn't change their name.) |
1286
|
|
|
require_once(SUBSDIR . '/Members.subs.php'); |
1287
|
|
|
updateMemberStats(); |
1288
|
|
|
|
1289
|
|
|
// If they haven't been deleted, update the post group statistics on them... |
1290
|
|
|
if (!in_array($todo, ['delete', 'deleteemail', 'reject', 'rejectemail', 'remind'])) |
1291
|
|
|
{ |
1292
|
|
|
require_once(SUBSDIR . '/Membergroups.subs.php'); |
1293
|
|
|
updatePostGroupStats($this->conditions['members']); |
1294
|
|
|
} |
1295
|
|
|
|
1296
|
|
|
redirectexit('action=admin;area=viewmembers;sa=browse;type=' . $type . ';sort=' . $sort . ';filter=' . $current_filter . ';start=' . $start); |
1297
|
|
|
} |
1298
|
|
|
|
1299
|
|
|
/** |
1300
|
|
|
* Approve a member application |
1301
|
|
|
*/ |
1302
|
|
|
private function _okMember() |
1303
|
|
|
{ |
1304
|
|
|
// Approve / activate this member. |
1305
|
|
|
approveMembers($this->conditions); |
1306
|
|
|
|
1307
|
|
|
// Check for email. |
1308
|
|
|
if ($this->_req->post->todo === 'okemail') |
1309
|
|
|
{ |
1310
|
|
|
foreach ($this->member_info as $member) |
1311
|
|
|
{ |
1312
|
|
|
$replacements = array( |
1313
|
|
|
'NAME' => $member['name'], |
1314
|
|
|
'USERNAME' => $member['username'], |
1315
|
|
|
'PROFILELINK' => getUrl('profile', ['action' => 'profile', 'u' => $member['id'], 'name' => $member['name']]), |
1316
|
|
|
'FORGOTPASSWORDLINK' => getUrl('action', ['action' => 'reminder']), |
1317
|
|
|
); |
1318
|
|
|
|
1319
|
|
|
$emaildata = loadEmailTemplate('admin_approve_accept', $replacements, $member['language']); |
1320
|
|
|
sendmail($member['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 0); |
1321
|
|
|
} |
1322
|
|
|
} |
1323
|
|
|
|
1324
|
|
|
// Update the menu action cache so its forced to refresh |
1325
|
|
|
Cache::instance()->remove('num_menu_errors'); |
1326
|
|
|
} |
1327
|
|
|
|
1328
|
|
|
/** |
1329
|
|
|
* Tell some members that they require activation of their account |
1330
|
|
|
*/ |
1331
|
|
|
private function _requireMember() |
1332
|
|
|
{ |
1333
|
|
|
require_once(SUBSDIR . '/Auth.subs.php'); |
1334
|
|
|
|
1335
|
|
|
// We have to do this for each member I'm afraid. |
1336
|
|
|
foreach ($this->member_info as $member) |
1337
|
|
|
{ |
1338
|
|
|
$this->conditions['selected_member'] = $member['id']; |
1339
|
|
|
|
1340
|
|
|
// Generate a random activation code. |
1341
|
|
|
$this->conditions['validation_code'] = generateValidationCode(14); |
1342
|
|
|
|
1343
|
|
|
// Set these members for activation - I know this includes two id_member checks but it's safer than bodging $condition ;). |
1344
|
|
|
enforceReactivation($this->conditions); |
1345
|
|
|
|
1346
|
|
|
$replacements = array( |
1347
|
|
|
'USERNAME' => $member['name'], |
1348
|
|
|
'ACTIVATIONLINK' => getUrl('action', ['action' => 'register', 'sa' => 'activate', 'u' => $member['id'], 'code' => $this->conditions['validation_code']]), |
1349
|
|
|
'ACTIVATIONLINKWITHOUTCODE' => getUrl('action', ['action' => 'register', 'sa' => 'activate', 'u' => $member['id']]), |
1350
|
|
|
'ACTIVATIONCODE' => $this->conditions['validation_code'], |
1351
|
|
|
); |
1352
|
|
|
|
1353
|
|
|
$emaildata = loadEmailTemplate('admin_approve_activation', $replacements, $member['language']); |
1354
|
|
|
sendmail($member['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 0); |
1355
|
|
|
} |
1356
|
|
|
} |
1357
|
|
|
|
1358
|
|
|
/** |
1359
|
|
|
* Reject a set a member applications, maybe even tell them |
1360
|
|
|
*/ |
1361
|
|
|
private function _rejectMember() |
1362
|
|
|
{ |
1363
|
|
|
deleteMembers($this->conditions['members']); |
1364
|
|
|
|
1365
|
|
|
// Send email telling them they aren't welcome? |
1366
|
|
|
if ($this->_req->post->todo === 'rejectemail') |
1367
|
|
|
{ |
1368
|
|
|
foreach ($this->member_info as $member) |
1369
|
|
|
{ |
1370
|
|
|
$replacements = array( |
1371
|
|
|
'USERNAME' => $member['name'], |
1372
|
|
|
); |
1373
|
|
|
|
1374
|
|
|
$emaildata = loadEmailTemplate('admin_approve_reject', $replacements, $member['language']); |
1375
|
|
|
sendmail($member['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 1); |
1376
|
|
|
} |
1377
|
|
|
} |
1378
|
|
|
} |
1379
|
|
|
|
1380
|
|
|
/** |
1381
|
|
|
* Deletes the members specified in the conditions array. |
1382
|
|
|
* |
1383
|
|
|
* What it does: |
1384
|
|
|
* |
1385
|
|
|
* - Called by the action_approve method. |
1386
|
|
|
* - Deletes the members specified in the conditions array. |
1387
|
|
|
* - Optionally sends email to notify the deleted members. |
1388
|
|
|
* |
1389
|
|
|
* @return void |
1390
|
|
|
*/ |
1391
|
|
|
private function _deleteMember() |
1392
|
|
|
{ |
1393
|
|
|
deleteMembers($this->conditions['members']); |
1394
|
|
|
|
1395
|
|
|
// Send email telling them they aren't welcome? |
1396
|
|
|
if ($this->_req->post->todo === 'deleteemail') |
1397
|
|
|
{ |
1398
|
|
|
foreach ($this->member_info as $member) |
1399
|
|
|
{ |
1400
|
|
|
$replacements = array( |
1401
|
|
|
'USERNAME' => $member['name'], |
1402
|
|
|
); |
1403
|
|
|
|
1404
|
|
|
$emaildata = loadEmailTemplate('admin_approve_delete', $replacements, $member['language']); |
1405
|
|
|
sendmail($member['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 1); |
1406
|
|
|
} |
1407
|
|
|
} |
1408
|
|
|
} |
1409
|
|
|
|
1410
|
|
|
/** |
1411
|
|
|
* Remind a set of members that they have an activation email waiting |
1412
|
|
|
*/ |
1413
|
|
|
private function _remindMember() |
1414
|
|
|
{ |
1415
|
|
|
require_once(SUBSDIR . '/Auth.subs.php'); |
1416
|
|
|
|
1417
|
|
|
foreach ($this->member_info as $member) |
1418
|
|
|
{ |
1419
|
|
|
$this->conditions['selected_member'] = $member['id']; |
1420
|
|
|
$this->conditions['validation_code'] = generateValidationCode(14); |
1421
|
|
|
|
1422
|
|
|
enforceReactivation($this->conditions); |
1423
|
|
|
|
1424
|
|
|
$replacements = array( |
1425
|
|
|
'USERNAME' => $member['name'], |
1426
|
|
|
'ACTIVATIONLINK' => getUrl('action', ['action' => 'register', 'sa' => 'activate', 'u' => $member['id'], 'code' => $this->conditions['validation_code']]), |
1427
|
|
|
'ACTIVATIONLINKWITHOUTCODE' => getUrl('action', ['action' => 'register', 'sa' => 'activate', 'u' => $member['id']]), |
1428
|
|
|
'ACTIVATIONCODE' => $this->conditions['validation_code'], |
1429
|
|
|
); |
1430
|
|
|
|
1431
|
|
|
$emaildata = loadEmailTemplate('admin_approve_remind', $replacements, $member['language']); |
1432
|
|
|
sendmail($member['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 1); |
1433
|
|
|
} |
1434
|
|
|
} |
1435
|
|
|
} |
1436
|
|
|
|