PageRenderTime 82ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/forum/Sources/ManageMembers.php

https://github.com/leftnode/nooges.com
PHP | 1316 lines | 1043 code | 106 blank | 167 comment | 160 complexity | 7c9138b5586552ec2fb8db45b4a34652 MD5 | raw file
  1. <?php
  2. /**********************************************************************************
  3. * ManageMembers.php *
  4. ***********************************************************************************
  5. * SMF: Simple Machines Forum *
  6. * Open-Source Project Inspired by Zef Hemel (zef@zefhemel.com) *
  7. * =============================================================================== *
  8. * Software Version: SMF 2.0 RC2 *
  9. * Software by: Simple Machines (http://www.simplemachines.org) *
  10. * Copyright 2006-2009 by: Simple Machines LLC (http://www.simplemachines.org) *
  11. * 2001-2006 by: Lewis Media (http://www.lewismedia.com) *
  12. * Support, News, Updates at: http://www.simplemachines.org *
  13. ***********************************************************************************
  14. * This program is free software; you may redistribute it and/or modify it under *
  15. * the terms of the provided license as published by Simple Machines LLC. *
  16. * *
  17. * This program is distributed in the hope that it is and will be useful, but *
  18. * WITHOUT ANY WARRANTIES; without even any implied warranty of MERCHANTABILITY *
  19. * or FITNESS FOR A PARTICULAR PURPOSE. *
  20. * *
  21. * See the "license.txt" file for details of the Simple Machines license. *
  22. * The latest version can always be found at http://www.simplemachines.org. *
  23. **********************************************************************************/
  24. if (!defined('SMF'))
  25. die('Hacking attempt...');
  26. /* // Show a list of members or a selection of members.
  27. void ViewMembers()
  28. - the main entrance point for the Manage Members screen.
  29. - called by ?action=admin;area=viewmembers.
  30. - requires the moderate_forum permission.
  31. - loads the ManageMembers template and ManageMembers language file.
  32. - calls a function based on the given sub-action.
  33. void ViewMemberlist()
  34. - shows a list of members.
  35. - called by ?action=admin;area=viewmembers;sa=all or ?action=admin;area=viewmembers;sa=query.
  36. - requires the moderate_forum permission.
  37. - uses the view_members sub template of the ManageMembers template.
  38. - allows sorting on several columns.
  39. - handles deletion of selected members.
  40. - handles the search query sent by ?action=admin;area=viewmembers;sa=search.
  41. void SearchMembers()
  42. - search the member list, using one or more criteria.
  43. - called by ?action=admin;area=viewmembers;sa=search.
  44. - requires the moderate_forum permission.
  45. - uses the search_members sub template of the ManageMembers template.
  46. - form is submitted to action=admin;area=viewmembers;sa=query.
  47. void MembersAwaitingActivation()
  48. - show a list of members awaiting approval or activation.
  49. - called by ?action=admin;area=viewmembers;sa=browse;type=approve or
  50. ?action=admin;area=viewmembers;sa=browse;type=activate.
  51. - requires the moderate_forum permission.
  52. - uses the admin_browse sub template of the ManageMembers template.
  53. - allows instant approval or activation of (a selection of) members.
  54. - list can be sorted on different columns.
  55. - form submits to ?action=admin;area=viewmembers;sa=approve.
  56. void AdminApprove()
  57. - handles the approval, rejection, activation or deletion of members.
  58. - called by ?action=admin;area=viewmembers;sa=approve.
  59. - requires the moderate_forum permission.
  60. - redirects to ?action=admin;area=viewmembers;sa=browse with the same parameters
  61. as the calling page.
  62. int jeffsdatediff(int old)
  63. - nifty function to calculate the number of days ago a given date was.
  64. - requires a unix timestamp as input, returns an integer.
  65. - in honour of Jeff Lewis, the original creator of...this function.
  66. - the returned number of days is based on the forum time.
  67. */
  68. function ViewMembers()
  69. {
  70. global $txt, $scripturl, $context, $modSettings, $smcFunc;
  71. $subActions = array(
  72. 'all' => array('ViewMemberlist', 'moderate_forum'),
  73. 'approve' => array('AdminApprove', 'moderate_forum'),
  74. 'browse' => array('MembersAwaitingActivation', 'moderate_forum'),
  75. 'search' => array('SearchMembers', 'moderate_forum'),
  76. 'query' => array('ViewMemberlist', 'moderate_forum'),
  77. );
  78. // Default to sub action 'index' or 'settings' depending on permissions.
  79. $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'all';
  80. // We know the sub action, now we know what you're allowed to do.
  81. isAllowedTo($subActions[$_REQUEST['sa']][1]);
  82. // Load the essentials.
  83. loadLanguage('ManageMembers');
  84. loadTemplate('ManageMembers');
  85. // Get counts on every type of activation - for sections and filtering alike.
  86. $request = $smcFunc['db_query']('', '
  87. SELECT COUNT(*) AS total_members, is_activated
  88. FROM {db_prefix}members
  89. WHERE is_activated != {int:is_activated}
  90. GROUP BY is_activated',
  91. array(
  92. 'is_activated' => 1,
  93. )
  94. );
  95. $context['activation_numbers'] = array();
  96. $context['awaiting_activation'] = 0;
  97. $context['awaiting_approval'] = 0;
  98. while ($row = $smcFunc['db_fetch_assoc']($request))
  99. $context['activation_numbers'][$row['is_activated']] = $row['total_members'];
  100. $smcFunc['db_free_result']($request);
  101. foreach ($context['activation_numbers'] as $activation_type => $total_members)
  102. {
  103. if (in_array($activation_type, array(0, 2)))
  104. $context['awaiting_activation'] += $total_members;
  105. elseif (in_array($activation_type, array(3, 4, 5)))
  106. $context['awaiting_approval'] += $total_members;
  107. }
  108. // For the page header... do we show activation?
  109. $context['show_activate'] = (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 1) || !empty($context['awaiting_activation']);
  110. // What about approval?
  111. $context['show_approve'] = (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 2) || !empty($context['awaiting_approval']);
  112. // Setup the admin tabs.
  113. $context[$context['admin_menu_name']]['tab_data'] = array(
  114. 'title' => $txt['admin_members'],
  115. 'help' => 'view_members',
  116. 'description' => $txt['admin_members_list'],
  117. 'tabs' => array(),
  118. );
  119. $context['tabs'] = array(
  120. 'viewmembers' => array(
  121. 'label' => $txt['view_all_members'],
  122. 'description' => $txt['admin_members_list'],
  123. 'url' => $scripturl . '?action=admin;area=viewmembers;sa=all',
  124. 'is_selected' => $_REQUEST['sa'] == 'all',
  125. ),
  126. 'search' => array(
  127. 'label' => $txt['mlist_search'],
  128. 'description' => $txt['admin_members_list'],
  129. 'url' => $scripturl . '?action=admin;area=viewmembers;sa=search',
  130. 'is_selected' => $_REQUEST['sa'] == 'search' || $_REQUEST['sa'] == 'query',
  131. ),
  132. 'approve' => array(
  133. 'label' => sprintf($txt['admin_browse_awaiting_approval'], $context['awaiting_approval']),
  134. 'description' => $txt['admin_browse_approve_desc'],
  135. 'url' => $scripturl . '?action=admin;area=viewmembers;sa=browse;type=approve',
  136. 'is_selected' => false,
  137. ),
  138. 'activate' => array(
  139. 'label' => sprintf($txt['admin_browse_awaiting_activate'], $context['awaiting_activation']),
  140. 'description' => $txt['admin_browse_activate_desc'],
  141. 'url' => $scripturl . '?action=admin;area=viewmembers;sa=browse;type=activate',
  142. 'is_selected' => false,
  143. 'is_last' => true,
  144. ),
  145. );
  146. // Sort out the tabs for the ones which may not exist!
  147. if (!$context['show_activate'] && ($_REQUEST['sa'] != 'browse' || $_REQUEST['type'] != 'activate'))
  148. {
  149. $context['tabs']['approve']['is_last'] = true;
  150. unset($context['tabs']['activate']);
  151. }
  152. if (!$context['show_approve'] && ($_REQUEST['sa'] != 'browse' || $_REQUEST['type'] != 'approve'))
  153. {
  154. if (!$context['show_activate'] && ($_REQUEST['sa'] != 'browse' || $_REQUEST['type'] != 'activate'))
  155. $context['tabs']['search']['is_last'] = true;
  156. unset($context['tabs']['approve']);
  157. }
  158. $subActions[$_REQUEST['sa']][0]();
  159. }
  160. // View all members.
  161. function ViewMemberlist()
  162. {
  163. global $txt, $scripturl, $context, $modSettings, $sourcedir, $smcFunc, $user_info;
  164. // Set the current sub action.
  165. $context['sub_action'] = $_REQUEST['sa'];
  166. // Are we performing a delete?
  167. if (isset($_POST['delete_members']) && !empty($_POST['delete']) && allowedTo('profile_remove_any'))
  168. {
  169. checkSession();
  170. // Clean the input.
  171. foreach ($_POST['delete'] as $key => $value)
  172. {
  173. $_POST['delete'][$key] = (int) $value;
  174. // Don't delete yourself, idiot.
  175. if ($value == $user_info['id'])
  176. unset($_POST['delete'][$key]);
  177. }
  178. if (!empty($_POST['delete']))
  179. {
  180. // Delete all the selected members.
  181. require_once($sourcedir . '/Subs-Members.php');
  182. deleteMembers($_POST['delete'], true);
  183. }
  184. }
  185. if ($context['sub_action'] == 'query' && !empty($_REQUEST['params']) && empty($_POST))
  186. $_POST += @unserialize(base64_decode($_REQUEST['params']));
  187. // Check input after a member search has been submitted.
  188. if ($context['sub_action'] == 'query')
  189. {
  190. // Retrieving the membergroups and postgroups.
  191. $context['membergroups'] = array(
  192. array(
  193. 'id' => 0,
  194. 'name' => $txt['membergroups_members'],
  195. 'can_be_additional' => false
  196. )
  197. );
  198. $context['postgroups'] = array();
  199. $request = $smcFunc['db_query']('', '
  200. SELECT id_group, group_name, min_posts
  201. FROM {db_prefix}membergroups
  202. WHERE id_group != {int:moderator_group}
  203. ORDER BY min_posts, CASE WHEN id_group < {int:newbie_group} THEN id_group ELSE 4 END, group_name',
  204. array(
  205. 'moderator_group' => 3,
  206. 'newbie_group' => 4,
  207. )
  208. );
  209. while ($row = $smcFunc['db_fetch_assoc']($request))
  210. {
  211. if ($row['min_posts'] == -1)
  212. $context['membergroups'][] = array(
  213. 'id' => $row['id_group'],
  214. 'name' => $row['group_name'],
  215. 'can_be_additional' => true
  216. );
  217. else
  218. $context['postgroups'][] = array(
  219. 'id' => $row['id_group'],
  220. 'name' => $row['group_name']
  221. );
  222. }
  223. $smcFunc['db_free_result']($request);
  224. // Some data about the form fields and how they are linked to the database.
  225. $params = array(
  226. 'mem_id' => array(
  227. 'db_fields' => array('id_member'),
  228. 'type' => 'int',
  229. 'range' => true
  230. ),
  231. 'age' => array(
  232. 'db_fields' => array('birthdate'),
  233. 'type' => 'age',
  234. 'range' => true
  235. ),
  236. 'posts' => array(
  237. 'db_fields' => array('posts'),
  238. 'type' => 'int',
  239. 'range' => true
  240. ),
  241. 'reg_date' => array(
  242. 'db_fields' => array('date_registered'),
  243. 'type' => 'date',
  244. 'range' => true
  245. ),
  246. 'last_online' => array(
  247. 'db_fields' => array('last_login'),
  248. 'type' => 'date',
  249. 'range' => true
  250. ),
  251. 'gender' => array(
  252. 'db_fields' => array('gender'),
  253. 'type' => 'checkbox',
  254. 'values' => array('0', '1', '2'),
  255. ),
  256. 'activated' => array(
  257. 'db_fields' => array('CASE WHEN is_activated IN (1, 11) THEN 1 ELSE 0 END'),
  258. 'type' => 'checkbox',
  259. 'values' => array('0', '1'),
  260. ),
  261. 'membername' => array(
  262. 'db_fields' => array('member_name', 'real_name'),
  263. 'type' => 'string'
  264. ),
  265. 'email' => array(
  266. 'db_fields' => array('email_address'),
  267. 'type' => 'string'
  268. ),
  269. 'website' => array(
  270. 'db_fields' => array('website_title', 'website_url'),
  271. 'type' => 'string'
  272. ),
  273. 'location' => array(
  274. 'db_fields' => array('location'),
  275. 'type' => 'string'
  276. ),
  277. 'ip' => array(
  278. 'db_fields' => array('member_ip'),
  279. 'type' => 'string'
  280. ),
  281. 'messenger' => array(
  282. 'db_fields' => array('icq', 'aim', 'yim', 'msn'),
  283. 'type' => 'string'
  284. )
  285. );
  286. $range_trans = array(
  287. '--' => '<',
  288. '-' => '<=',
  289. '=' => '=',
  290. '+' => '>=',
  291. '++' => '>'
  292. );
  293. // !!! Validate a little more.
  294. // Loop through every field of the form.
  295. $query_parts = array();
  296. $where_params = array();
  297. foreach ($params as $param_name => $param_info)
  298. {
  299. // Not filled in?
  300. if (!isset($_POST[$param_name]) || $_POST[$param_name] === '')
  301. continue;
  302. // Make sure numeric values are really numeric.
  303. if (in_array($param_info['type'], array('int', 'age')))
  304. $_POST[$param_name] = (int) $_POST[$param_name];
  305. // Date values have to match the specified format.
  306. elseif ($param_info['type'] == 'date')
  307. {
  308. // Check if this date format is valid.
  309. if (preg_match('/^\d{4}-\d{1,2}-\d{1,2}$/', $_POST[$param_name]) == 0)
  310. continue;
  311. $_POST[$param_name] = strtotime($_POST[$param_name]);
  312. }
  313. // Those values that are in some kind of range (<, <=, =, >=, >).
  314. if (!empty($param_info['range']))
  315. {
  316. // Default to '=', just in case...
  317. if (empty($range_trans[$_POST['types'][$param_name]]))
  318. $_POST['types'][$param_name] = '=';
  319. // Handle special case 'age'.
  320. if ($param_info['type'] == 'age')
  321. {
  322. // All people that were born between $lowerlimit and $upperlimit are currently the specified age.
  323. $datearray = getdate(forum_time());
  324. $upperlimit = sprintf('%04d-%02d-%02d', $datearray['year'] - $_POST[$param_name], $datearray['mon'], $datearray['mday']);
  325. $lowerlimit = sprintf('%04d-%02d-%02d', $datearray['year'] - $_POST[$param_name] - 1, $datearray['mon'], $datearray['mday']);
  326. if (in_array($_POST['types'][$param_name], array('-', '--', '=')))
  327. {
  328. $query_parts[] = ($param_info['db_fields'][0]) . ' > {string:' . $param_name . '_minlimit}';
  329. $where_params[$param_name . '_minlimit'] = ($_POST['types'][$param_name] == '--' ? $upperlimit : $lowerlimit);
  330. }
  331. if (in_array($_POST['types'][$param_name], array('+', '++', '=')))
  332. {
  333. $query_parts[] = ($param_info['db_fields'][0]) . ' <= {string:' . $param_name . '_pluslimit}';
  334. $where_params[$param_name . '_pluslimit'] = ($_POST['types'][$param_name] == '++' ? $lowerlimit : $upperlimit);
  335. // Make sure that members that didn't set their birth year are not queried.
  336. $query_parts[] = ($param_info['db_fields'][0]) . ' > {date:dec_zero_date}';
  337. $where_params['dec_zero_date'] = '0004-12-31';
  338. }
  339. }
  340. // Special case - equals a date.
  341. elseif ($param_info['type'] == 'date' && $_POST['types'][$param_name] == '=')
  342. {
  343. $query_parts[] = $param_info['db_fields'][0] . ' > ' . $_POST[$param_name] . ' AND ' . $param_info['db_fields'][0] . ' < ' . ($_POST[$param_name] + 86400);
  344. }
  345. else
  346. $query_parts[] = $param_info['db_fields'][0] . ' ' . $range_trans[$_POST['types'][$param_name]] . ' ' . $_POST[$param_name];
  347. }
  348. // Checkboxes.
  349. elseif ($param_info['type'] == 'checkbox')
  350. {
  351. // Each checkbox or no checkbox at all is checked -> ignore.
  352. if (!is_array($_POST[$param_name]) || count($_POST[$param_name]) == 0 || count($_POST[$param_name]) == count($param_info['values']))
  353. continue;
  354. $query_parts[] = ($param_info['db_fields'][0]) . ' IN ({array_string:' . $param_name . '_check})';
  355. $where_params[$param_name . '_check'] = $_POST[$param_name];
  356. }
  357. else
  358. {
  359. // Replace the wildcard characters ('*' and '?') into MySQL ones.
  360. $_POST[$param_name] = strtolower(strtr($smcFunc['htmlspecialchars']($_POST[$param_name], ENT_QUOTES), array('%' => '\%', '_' => '\_', '*' => '%', '?' => '_')));
  361. $query_parts[] = '(' . implode( ' LIKE {string:' . $param_name . '_normal} OR ', $param_info['db_fields']) . ' LIKE {string:' . $param_name . '_normal})';
  362. $where_params[$param_name . '_normal'] = '%' . $_POST[$param_name] . '%';
  363. }
  364. }
  365. // Set up the membergroup query part.
  366. $mg_query_parts = array();
  367. // Primary membergroups, but only if at least was was not selected.
  368. if (!empty($_POST['membergroups'][1]) && count($context['membergroups']) != count($_POST['membergroups'][1]))
  369. {
  370. $mg_query_parts[] = 'mem.id_group IN ({array_int:group_check})';
  371. $where_params['group_check'] = $_POST['membergroups'][1];
  372. }
  373. // Additional membergroups (these are only relevant if not all primary groups where selected!).
  374. if (!empty($_POST['membergroups'][2]) && (empty($_POST['membergroups'][1]) || count($context['membergroups']) != count($_POST['membergroups'][1])))
  375. foreach ($_POST['membergroups'][2] as $mg)
  376. {
  377. $mg_query_parts[] = 'FIND_IN_SET({int:add_group_' . $mg . '}, mem.additional_groups)';
  378. $where_params['add_group_' . $mg] = $mg;
  379. }
  380. // Combine the one or two membergroup parts into one query part linked with an OR.
  381. if (!empty($mg_query_parts))
  382. $query_parts[] = '(' . implode(' OR ', $mg_query_parts) . ')';
  383. // Get all selected post count related membergroups.
  384. if (!empty($_POST['postgroups']) && count($_POST['postgroups']) != count($context['postgroups']))
  385. {
  386. $query_parts[] = 'id_post_group IN ({array_int:post_groups})';
  387. $where_params['post_groups'] = $_POST['postgroups'];
  388. }
  389. // Construct the where part of the query.
  390. $where = empty($query_parts) ? '1' : implode('
  391. AND ', $query_parts);
  392. $search_params = base64_encode(serialize($_POST));
  393. }
  394. else
  395. $search_params = null;
  396. // Construct the additional URL part with the query info in it.
  397. $context['params_url'] = $context['sub_action'] == 'query' ? ';sa=query;params=' . $search_params : '';
  398. // Get the title and sub template ready..
  399. $context['page_title'] = $txt['admin_members'];
  400. $listOptions = array(
  401. 'id' => 'member_list',
  402. 'items_per_page' => $modSettings['defaultMaxMembers'],
  403. 'base_href' => $scripturl . '?action=admin;area=viewmembers' . $context['params_url'],
  404. 'default_sort_col' => 'user_name',
  405. 'get_items' => array(
  406. 'file' => $sourcedir . '/Subs-Members.php',
  407. 'function' => 'list_getMembers',
  408. 'params' => array(
  409. isset($where) ? $where : '1=1',
  410. isset($where_params) ? $where_params : array(),
  411. ),
  412. ),
  413. 'get_count' => array(
  414. 'file' => $sourcedir . '/Subs-Members.php',
  415. 'function' => 'list_getNumMembers',
  416. 'params' => array(
  417. isset($where) ? $where : '1=1',
  418. isset($where_params) ? $where_params : array(),
  419. ),
  420. ),
  421. 'columns' => array(
  422. 'id_member' => array(
  423. 'header' => array(
  424. 'value' => $txt['member_id'],
  425. ),
  426. 'data' => array(
  427. 'db' => 'id_member',
  428. 'class' => 'windowbg',
  429. 'style' => 'text-align: center;',
  430. ),
  431. 'sort' => array(
  432. 'default' => 'id_member',
  433. 'reverse' => 'id_member DESC',
  434. ),
  435. ),
  436. 'user_name' => array(
  437. 'header' => array(
  438. 'value' => $txt['username'],
  439. ),
  440. 'data' => array(
  441. 'sprintf' => array(
  442. 'format' => '<a href="' . strtr($scripturl, array('%' => '%%')) . '?action=profile;u=%1$d">%2$s</a>',
  443. 'params' => array(
  444. 'id_member' => false,
  445. 'member_name' => false,
  446. ),
  447. ),
  448. ),
  449. 'sort' => array(
  450. 'default' => 'member_name',
  451. 'reverse' => 'member_name DESC',
  452. ),
  453. ),
  454. 'display_name' => array(
  455. 'header' => array(
  456. 'value' => $txt['display_name'],
  457. ),
  458. 'data' => array(
  459. 'sprintf' => array(
  460. 'format' => '<a href="' . strtr($scripturl, array('%' => '%%')) . '?action=profile;u=%1$d">%2$s</a>',
  461. 'params' => array(
  462. 'id_member' => false,
  463. 'real_name' => false,
  464. ),
  465. ),
  466. ),
  467. 'sort' => array(
  468. 'default' => 'real_name',
  469. 'reverse' => 'real_name DESC',
  470. ),
  471. ),
  472. 'email' => array(
  473. 'header' => array(
  474. 'value' => $txt['email_address'],
  475. ),
  476. 'data' => array(
  477. 'sprintf' => array(
  478. 'format' => '<a href="mailto:%1$s">%1$s</a>',
  479. 'params' => array(
  480. 'email_address' => true,
  481. ),
  482. ),
  483. 'class' => 'windowbg',
  484. ),
  485. 'sort' => array(
  486. 'default' => 'email_address',
  487. 'reverse' => 'email_address DESC',
  488. ),
  489. ),
  490. 'ip' => array(
  491. 'header' => array(
  492. 'value' => $txt['ip_address'],
  493. ),
  494. 'data' => array(
  495. 'sprintf' => array(
  496. 'format' => '<a href="' . strtr($scripturl, array('%' => '%%')) . '?action=trackip;searchip=%1$s">%1$s</a>',
  497. 'params' => array(
  498. 'member_ip' => false,
  499. ),
  500. ),
  501. ),
  502. 'sort' => array(
  503. 'default' => 'INET_ATON(member_ip)',
  504. 'reverse' => 'INET_ATON(member_ip) DESC',
  505. ),
  506. ),
  507. 'last_active' => array(
  508. 'header' => array(
  509. 'value' => $txt['viewmembers_online'],
  510. ),
  511. 'data' => array(
  512. 'function' => create_function('$rowData', '
  513. global $txt;
  514. // Calculate number of days since last online.
  515. if (empty($rowData[\'last_login\']))
  516. $difference = $txt[\'never\'];
  517. else
  518. {
  519. $num_days_difference = jeffsdatediff($rowData[\'last_login\']);
  520. // Today.
  521. if (empty($num_days_difference))
  522. $difference = $txt[\'viewmembers_today\'];
  523. // Yesterday.
  524. elseif ($num_days_difference == 1)
  525. $difference = sprintf(\'1 %1$s\', $txt[\'viewmembers_day_ago\']);
  526. // X days ago.
  527. else
  528. $difference = sprintf(\'%1$d %2$s\', $num_days_difference, $txt[\'viewmembers_days_ago\']);
  529. }
  530. // Show it in italics if they\'re not activated...
  531. if ($rowData[\'is_activated\'] % 10 != 1)
  532. $difference = sprintf(\'<em title="%1$s">%2$s</em>\', $txt[\'not_activated\'], $difference);
  533. return $difference;
  534. '),
  535. ),
  536. 'sort' => array(
  537. 'default' => 'last_login DESC',
  538. 'reverse' => 'last_login',
  539. ),
  540. ),
  541. 'posts' => array(
  542. 'header' => array(
  543. 'value' => $txt['member_postcount'],
  544. ),
  545. 'data' => array(
  546. 'db' => 'posts',
  547. ),
  548. 'sort' => array(
  549. 'default' => 'posts',
  550. 'reverse' => 'posts DESC',
  551. ),
  552. ),
  553. 'check' => array(
  554. 'header' => array(
  555. 'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" class="input_check" />',
  556. ),
  557. 'data' => array(
  558. 'function' => create_function('$rowData', '
  559. global $user_info;
  560. return \'<input type="checkbox" name="delete[]" 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"\' : \'\') . \' />\';
  561. '),
  562. 'class' => 'windowbg',
  563. 'style' => 'text-align: center',
  564. ),
  565. ),
  566. ),
  567. 'form' => array(
  568. 'href' => $scripturl . '?action=admin;area=viewmembers' . $context['params_url'],
  569. 'include_start' => true,
  570. 'include_sort' => true,
  571. ),
  572. 'additional_rows' => array(
  573. array(
  574. 'position' => 'below_table_data',
  575. 'value' => '<input type="submit" name="delete_members" value="' . $txt['admin_delete_members'] . '" onclick="return confirm(\'' . $txt['confirm_delete_members'] . '\');" class="button_submit" />',
  576. 'class' => 'titlebg',
  577. 'style' => 'text-align: right;',
  578. ),
  579. ),
  580. );
  581. // Without not enough permissions, don't show 'delete members' checkboxes.
  582. if (!allowedTo('profile_remove_any'))
  583. unset($listOptions['cols']['check'], $listOptions['form'], $listOptions['additional_rows']);
  584. require_once($sourcedir . '/Subs-List.php');
  585. createList($listOptions);
  586. $context['sub_template'] = 'show_list';
  587. $context['default_list'] = 'member_list';
  588. }
  589. // Search the member list, using one or more criteria.
  590. function SearchMembers()
  591. {
  592. global $context, $txt, $smcFunc;
  593. // Get a list of all the membergroups and postgroups that can be selected.
  594. $context['membergroups'] = array(
  595. array(
  596. 'id' => 0,
  597. 'name' => $txt['membergroups_members'],
  598. 'can_be_additional' => false
  599. )
  600. );
  601. $context['postgroups'] = array();
  602. $request = $smcFunc['db_query']('', '
  603. SELECT id_group, group_name, min_posts
  604. FROM {db_prefix}membergroups
  605. WHERE id_group != {int:moderator_group}
  606. ORDER BY min_posts, CASE WHEN id_group < {int:newbie_group} THEN id_group ELSE 4 END, group_name',
  607. array(
  608. 'moderator_group' => 3,
  609. 'newbie_group' => 4,
  610. )
  611. );
  612. while ($row = $smcFunc['db_fetch_assoc']($request))
  613. {
  614. if ($row['min_posts'] == -1)
  615. $context['membergroups'][] = array(
  616. 'id' => $row['id_group'],
  617. 'name' => $row['group_name'],
  618. 'can_be_additional' => true
  619. );
  620. else
  621. $context['postgroups'][] = array(
  622. 'id' => $row['id_group'],
  623. 'name' => $row['group_name']
  624. );
  625. }
  626. $smcFunc['db_free_result']($request);
  627. $context['page_title'] = $txt['admin_members'];
  628. $context['sub_template'] = 'search_members';
  629. }
  630. // List all members who are awaiting approval / activation
  631. function MembersAwaitingActivation()
  632. {
  633. global $txt, $context, $scripturl, $modSettings, $smcFunc;
  634. global $sourcedir;
  635. // Not a lot here!
  636. $context['page_title'] = $txt['admin_members'];
  637. $context['sub_template'] = 'admin_browse';
  638. $context['browse_type'] = isset($_REQUEST['type']) ? $_REQUEST['type'] : (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 1 ? 'activate' : 'approve');
  639. if (isset($context['tabs'][$context['browse_type']]))
  640. $context['tabs'][$context['browse_type']]['is_selected'] = true;
  641. // Allowed filters are those we can have, in theory.
  642. $context['allowed_filters'] = $context['browse_type'] == 'approve' ? array(3, 4, 5) : array(0, 2);
  643. $context['current_filter'] = isset($_REQUEST['filter']) && in_array($_REQUEST['filter'], $context['allowed_filters']) && !empty($context['activation_numbers'][$_REQUEST['filter']]) ? (int) $_REQUEST['filter'] : -1;
  644. // Sort out the different sub areas that we can actually filter by.
  645. $context['available_filters'] = array();
  646. foreach ($context['activation_numbers'] as $type => $amount)
  647. {
  648. // We have some of these...
  649. if (in_array($type, $context['allowed_filters']) && $amount > 0)
  650. $context['available_filters'][] = array(
  651. 'type' => $type,
  652. 'amount' => $amount,
  653. 'desc' => isset($txt['admin_browse_filter_type_' . $type]) ? $txt['admin_browse_filter_type_' . $type] : '?',
  654. 'selected' => $type == $context['current_filter']
  655. );
  656. }
  657. // If the filter was not sent, set it to whatever has people in it!
  658. if ($context['current_filter'] == -1 && !empty($context['available_filters'][0]['amount']))
  659. $context['current_filter'] = $context['available_filters'][0]['type'];
  660. // This little variable is used to determine if we should flag where we are looking.
  661. $context['show_filter'] = ($context['current_filter'] != 0 && $context['current_filter'] != 3) || count($context['available_filters']) > 1;
  662. // The columns that can be sorted.
  663. $context['columns'] = array(
  664. 'id_member' => array('label' => $txt['admin_browse_id']),
  665. 'member_name' => array('label' => $txt['admin_browse_username']),
  666. 'email_address' => array('label' => $txt['admin_browse_email']),
  667. 'member_ip' => array('label' => $txt['admin_browse_ip']),
  668. 'date_registered' => array('label' => $txt['admin_browse_registered']),
  669. );
  670. // Are we showing duplicate information?
  671. if (isset($_GET['showdupes']))
  672. $_SESSION['showdupes'] = (int) $_GET['showdupes'];
  673. $context['show_duplicates'] = !empty($_SESSION['showdupes']);
  674. // Determine which actions we should allow on this page.
  675. if ($context['browse_type'] == 'approve')
  676. {
  677. // If we are approving deleted accounts we have a slightly different list... actually a mirror ;)
  678. if ($context['current_filter'] == 4)
  679. $context['allowed_actions'] = array(
  680. 'reject' => $txt['admin_browse_w_approve_deletion'],
  681. 'ok' => $txt['admin_browse_w_reject'],
  682. );
  683. else
  684. $context['allowed_actions'] = array(
  685. 'ok' => $txt['admin_browse_w_approve'],
  686. 'okemail' => $txt['admin_browse_w_approve'] . ' ' . $txt['admin_browse_w_email'],
  687. 'require_activation' => $txt['admin_browse_w_approve_require_activate'],
  688. 'reject' => $txt['admin_browse_w_reject'],
  689. 'rejectemail' => $txt['admin_browse_w_reject'] . ' ' . $txt['admin_browse_w_email'],
  690. );
  691. }
  692. elseif ($context['browse_type'] == 'activate')
  693. $context['allowed_actions'] = array(
  694. 'ok' => $txt['admin_browse_w_activate'],
  695. 'okemail' => $txt['admin_browse_w_activate'] . ' ' . $txt['admin_browse_w_email'],
  696. 'delete' => $txt['admin_browse_w_delete'],
  697. 'deleteemail' => $txt['admin_browse_w_delete'] . ' ' . $txt['admin_browse_w_email'],
  698. 'remind' => $txt['admin_browse_w_remind'] . ' ' . $txt['admin_browse_w_email'],
  699. );
  700. // Create an option list for actions allowed to be done with selected members.
  701. $allowed_actions = '
  702. <option selected="selected" value="">' . $txt['admin_browse_with_selected'] . ':</option>
  703. <option value="" disabled="disabled">-----------------------------</option>';
  704. foreach ($context['allowed_actions'] as $key => $desc)
  705. $allowed_actions .= '
  706. <option value="' . $key . '">' . $desc . '</option>';
  707. // Setup the Javascript function for selecting an action for the list.
  708. $javascript = '
  709. function onSelectChange()
  710. {
  711. if (document.forms.postForm.todo.value == "")
  712. return;
  713. var message = "";';
  714. // We have special messages for approving deletion of accounts - it's surprisingly logical - honest.
  715. if ($context['current_filter'] == 4)
  716. $javascript .= '
  717. if (document.forms.postForm.todo.value.indexOf("reject") != -1)
  718. message = "' . $txt['admin_browse_w_delete'] . '";
  719. else
  720. message = "' . $txt['admin_browse_w_reject'] . '";';
  721. // Otherwise a nice standard message.
  722. else
  723. $javascript .= '
  724. if (document.forms.postForm.todo.value.indexOf("delete") != -1)
  725. message = "' . $txt['admin_browse_w_delete'] . '";
  726. else if (document.forms.postForm.todo.value.indexOf("reject") != -1)
  727. message = "' . $txt['admin_browse_w_reject'] . '";
  728. else if (document.forms.postForm.todo.value == "remind")
  729. message = "' . $txt['admin_browse_w_remind'] . '";
  730. else
  731. message = "' . ($context['browse_type'] == 'approve' ? $txt['admin_browse_w_approve'] : $txt['admin_browse_w_activate']) . '";';
  732. $javascript .= '
  733. if (confirm(message + " ' . $txt['admin_browse_warn'] . '"))
  734. document.forms.postForm.submit();
  735. }';
  736. $listOptions = array(
  737. 'id' => 'approve_list',
  738. 'items_per_page' => $modSettings['defaultMaxMembers'],
  739. 'base_href' => $scripturl . '?action=admin;area=viewmembers;sa=browse;type=' . $context['browse_type'] . (!empty($context['show_filter']) ? ';filter=' . $context['current_filter'] : ''),
  740. 'default_sort_col' => 'date_registered',
  741. 'get_items' => array(
  742. 'file' => $sourcedir . '/Subs-Members.php',
  743. 'function' => 'list_getMembers',
  744. 'params' => array(
  745. 'is_activated = {int:activated_status}',
  746. array('activated_status' => $context['current_filter']),
  747. $context['show_duplicates'],
  748. ),
  749. ),
  750. 'get_count' => array(
  751. 'file' => $sourcedir . '/Subs-Members.php',
  752. 'function' => 'list_getNumMembers',
  753. 'params' => array(
  754. 'is_activated = {int:activated_status}',
  755. array('activated_status' => $context['current_filter']),
  756. ),
  757. ),
  758. 'columns' => array(
  759. 'id_member' => array(
  760. 'header' => array(
  761. 'value' => $txt['member_id'],
  762. ),
  763. 'data' => array(
  764. 'db' => 'id_member',
  765. 'class' => 'windowbg',
  766. 'style' => 'text-align: center;',
  767. ),
  768. 'sort' => array(
  769. 'default' => 'id_member',
  770. 'reverse' => 'id_member DESC',
  771. ),
  772. ),
  773. 'user_name' => array(
  774. 'header' => array(
  775. 'value' => $txt['username'],
  776. ),
  777. 'data' => array(
  778. 'sprintf' => array(
  779. 'format' => '<a href="' . strtr($scripturl, array('%' => '%%')) . '?action=profile;u=%1$d">%2$s</a>',
  780. 'params' => array(
  781. 'id_member' => false,
  782. 'member_name' => false,
  783. ),
  784. ),
  785. ),
  786. 'sort' => array(
  787. 'default' => 'member_name',
  788. 'reverse' => 'member_name DESC',
  789. ),
  790. ),
  791. 'email' => array(
  792. 'header' => array(
  793. 'value' => $txt['email_address'],
  794. ),
  795. 'data' => array(
  796. 'sprintf' => array(
  797. 'format' => '<a href="mailto:%1$s">%1$s</a>',
  798. 'params' => array(
  799. 'email_address' => true,
  800. ),
  801. ),
  802. 'class' => 'windowbg',
  803. ),
  804. 'sort' => array(
  805. 'default' => 'email_address',
  806. 'reverse' => 'email_address DESC',
  807. ),
  808. ),
  809. 'ip' => array(
  810. 'header' => array(
  811. 'value' => $txt['ip_address'],
  812. ),
  813. 'data' => array(
  814. 'sprintf' => array(
  815. 'format' => '<a href="' . strtr($scripturl, array('%' => '%%')) . '?action=trackip;searchip=%1$s">%1$s</a>',
  816. 'params' => array(
  817. 'member_ip' => false,
  818. ),
  819. ),
  820. ),
  821. 'sort' => array(
  822. 'default' => 'INET_ATON(member_ip)',
  823. 'reverse' => 'INET_ATON(member_ip) DESC',
  824. ),
  825. ),
  826. 'hostname' => array(
  827. 'header' => array(
  828. 'value' => $txt['hostname'],
  829. ),
  830. 'data' => array(
  831. 'function' => create_function('$rowData', '
  832. global $modSettings;
  833. return host_from_ip($rowData[\'member_ip\']);
  834. '),
  835. 'class' => 'smalltext',
  836. ),
  837. ),
  838. 'date_registered' => array(
  839. 'header' => array(
  840. 'value' => $txt['date_registered'],
  841. ),
  842. 'data' => array(
  843. 'function' => create_function('$rowData', '
  844. return timeformat($rowData[\'date_registered\']);
  845. '),
  846. ),
  847. 'sort' => array(
  848. 'default' => 'date_registered DESC',
  849. 'reverse' => 'date_registered',
  850. ),
  851. ),
  852. 'duplicates' => array(
  853. 'header' => array(
  854. 'value' => $txt['duplicates'],
  855. // Make sure it doesn't go too wide.
  856. 'style' => 'width: 20%',
  857. ),
  858. 'data' => array(
  859. 'function' => create_function('$rowData', '
  860. global $scripturl, $txt;
  861. $member_links = array();
  862. foreach ($rowData[\'duplicate_members\'] as $member)
  863. {
  864. if ($member[\'id\'])
  865. $member_links[] = \'<a href="\' . $scripturl . \'?action=profile;u=\' . $member[\'id\'] . \'" \' . (!empty($member[\'is_banned\']) ? \'style="color: red;"\' : \'\') . \'>\' . $member[\'name\'] . \'</a>\';
  866. else
  867. $member_links[] = $member[\'name\'] . \' (\' . $txt[\'guest\'] . \')\';
  868. }
  869. return implode (\', \', $member_links);
  870. '),
  871. 'class' => 'smalltext',
  872. ),
  873. ),
  874. 'check' => array(
  875. 'header' => array(
  876. 'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" class="input_check" />',
  877. ),
  878. 'data' => array(
  879. 'sprintf' => array(
  880. 'format' => '<input type="checkbox" name="todoAction[]" value="%1$d" class="input_check" />',
  881. 'params' => array(
  882. 'id_member' => false,
  883. ),
  884. ),
  885. 'class' => 'windowbg',
  886. 'style' => 'text-align: center',
  887. ),
  888. ),
  889. ),
  890. 'javascript' => $javascript,
  891. 'form' => array(
  892. 'href' => $scripturl . '?action=admin;area=viewmembers;sa=approve;type=' . $context['browse_type'],
  893. 'name' => 'postForm',
  894. 'include_start' => true,
  895. 'include_sort' => true,
  896. 'hidden_fields' => array(
  897. 'orig_filter' => $context['current_filter'],
  898. ),
  899. ),
  900. 'additional_rows' => array(
  901. array(
  902. 'position' => 'below_table_data',
  903. 'value' => '
  904. <div class="floatleft">
  905. [<a href="' . $scripturl . '?action=admin;area=viewmembers;sa=browse;showdupes=' . ($context['show_duplicates'] ? 0 : 1) . ';type=' . $context['browse_type'] . (!empty($context['show_filter']) ? ';filter=' . $context['current_filter'] : '') . ';' . $context['session_var'] . '=' . $context['session_id'] . '">' . ($context['show_duplicates'] ? $txt['dont_check_for_duplicate'] : $txt['check_for_duplicate']) . '</a>]
  906. </div>
  907. <div class="floatright">
  908. <select name="todo" onchange="onSelectChange();">
  909. ' . $allowed_actions . '
  910. </select>
  911. <noscript><input type="submit" value="' . $txt['go'] . '" class="button_submit" /></noscript>
  912. </div>',
  913. 'class' => 'titlebg',
  914. ),
  915. ),
  916. );
  917. // Pick what column to actually include if we're showing duplicates.
  918. if ($context['show_duplicates'])
  919. unset($listOptions['columns']['email']);
  920. else
  921. unset($listOptions['columns']['duplicates']);
  922. // Only show hostname on duplicates as it takes a lot of time.
  923. if (!$context['show_duplicates'] || !empty($modSettings['disableHostnameLookup']))
  924. unset($listOptions['columns']['hostname']);
  925. // Is there any need to show filters?
  926. if (isset($context['available_filters']) && count($context['available_filters']) > 1)
  927. {
  928. $filterOptions = '
  929. <strong>' . $txt['admin_browse_filter_by'] . ':</strong>
  930. <select name="filter" onchange="this.form.submit();">';
  931. foreach ($context['available_filters'] as $filter)
  932. $filterOptions .= '
  933. <option value="' . $filter['type'] . '"' . ($filter['selected'] ? ' selected="selected"' : '') . '>' . $filter['desc'] . ' - ' . $filter['amount'] . ' ' . ($filter['amount'] == 1 ? $txt['user'] : $txt['users']) . '</option>';
  934. $filterOptions .= '
  935. </select>
  936. <noscript><input type="submit" value="' . $txt['go'] . '" name="filter" class="button_submit" /></noscript>';
  937. $listOptions['additional_rows'][] = array(
  938. 'position' => 'above_column_headers',
  939. 'value' => $filterOptions,
  940. 'style' => 'text-align: center;',
  941. 'class' => 'titlebg',
  942. );
  943. }
  944. // What about if we only have one filter, but it's not the "standard" filter - show them what they are looking at.
  945. if (!empty($context['show_filter']) && !empty($context['available_filters']))
  946. $listOptions['additional_rows'][] = array(
  947. 'position' => 'above_column_headers',
  948. 'value' => '<span class="smalltext"><strong>' . $txt['admin_browse_filter_show'] . ':</strong> ' . $context['available_filters'][0]['desc'] . '</span>',
  949. 'style' => 'text-align: left;',
  950. 'class' => 'windowbg2',
  951. );
  952. // Now that we have all the options, create the list.
  953. require_once($sourcedir . '/Subs-List.php');
  954. createList($listOptions);
  955. }
  956. // Do the approve/activate/delete stuff
  957. function AdminApprove()
  958. {
  959. global $txt, $context, $scripturl, $modSettings, $sourcedir, $language, $user_info, $smcFunc;
  960. require_once($sourcedir . '/Subs-Post.php');
  961. // We also need to the login languages here - for emails.
  962. loadLanguage('Login');
  963. // Sort out where we are going...
  964. $browse_type = isset($_REQUEST['type']) ? $_REQUEST['type'] : (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 1 ? 'activate' : 'approve');
  965. $current_filter = (int) $_REQUEST['orig_filter'];
  966. // If we are applying a filter do just that - then redirect.
  967. if (isset($_REQUEST['filter']) && $_REQUEST['filter'] != $_REQUEST['orig_filter'])
  968. redirectexit('action=admin;area=viewmembers;sa=browse;type=' . $_REQUEST['type'] . ';sort=' . $_REQUEST['sort'] . ';filter=' . $_REQUEST['filter'] . ';start=' . $_REQUEST['start']);
  969. // Nothing to do?
  970. if (!isset($_POST['todoAction']) && !isset($_POST['time_passed']))
  971. redirectexit('action=admin;area=viewmembers;sa=browse;type=' . $_REQUEST['type'] . ';sort=' . $_REQUEST['sort'] . ';filter=' . $current_filter . ';start=' . $_REQUEST['start']);
  972. // Are we dealing with members who have been waiting for > set amount of time?
  973. if (isset($_POST['time_passed']))
  974. {
  975. $timeBefore = time() - 86400 * (int) $_POST['time_passed'];
  976. $condition = '
  977. AND date_registered < {int:time_before}';
  978. }
  979. // Coming from checkboxes - validate the members passed through to us.
  980. else
  981. {
  982. $members = array();
  983. foreach ($_POST['todoAction'] as $id)
  984. $members[] = (int) $id;
  985. $condition = '
  986. AND id_member IN ({array_int:members})';
  987. }
  988. // Get information on each of the members, things that are important to us, like email address...
  989. $request = $smcFunc['db_query']('', '
  990. SELECT id_member, member_name, real_name, email_address, validation_code, lngfile
  991. FROM {db_prefix}members
  992. WHERE is_activated = {int:activated_status}' . $condition . '
  993. ORDER BY lngfile',
  994. array(
  995. 'activated_status' => $current_filter,
  996. 'time_before' => empty($timeBefore) ? 0 : $timeBefore,
  997. 'members' => empty($members) ? array() : $members,
  998. )
  999. );
  1000. $member_count = $smcFunc['db_num_rows']($request);
  1001. // If no results then just return!
  1002. if ($member_count == 0)
  1003. redirectexit('action=admin;area=viewmembers;sa=browse;type=' . $_REQUEST['type'] . ';sort=' . $_REQUEST['sort'] . ';filter=' . $current_filter . ';start=' . $_REQUEST['start']);
  1004. $member_info = array();
  1005. $members = array();
  1006. // Fill the info array.
  1007. while ($row = $smcFunc['db_fetch_assoc']($request))
  1008. {
  1009. $members[] = $row['id_member'];
  1010. $member_info[] = array(
  1011. 'id' => $row['id_member'],
  1012. 'username' => $row['member_name'],
  1013. 'name' => $row['real_name'],
  1014. 'email' => $row['email_address'],
  1015. 'language' => empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile'],
  1016. 'code' => $row['validation_code']
  1017. );
  1018. }
  1019. $smcFunc['db_free_result']($request);
  1020. // Are we activating or approving the members?
  1021. if ($_POST['todo'] == 'ok' || $_POST['todo'] == 'okemail')
  1022. {
  1023. // Approve/activate this member.
  1024. $smcFunc['db_query']('', '
  1025. UPDATE {db_prefix}members
  1026. SET validation_code = {string:blank_string}, is_activated = {int:is_activated}
  1027. WHERE is_activated = {int:activated_status}' . $condition,
  1028. array(
  1029. 'is_activated' => 1,
  1030. 'time_before' => empty($timeBefore) ? 0 : $timeBefore,
  1031. 'members' => empty($members) ? array() : $members,
  1032. 'activated_status' => $current_filter,
  1033. 'blank_string' => '',
  1034. )
  1035. );
  1036. // Do we have to let the integration code know about the activations?
  1037. if (isset($modSettings['integrate_activate']) && function_exists($modSettings['integrate_activate']))
  1038. {
  1039. foreach ($member_info as $member)
  1040. call_user_func($modSettings['integrate_activate'], $member['username']);
  1041. }
  1042. // Check for email.
  1043. if ($_POST['todo'] == 'okemail')
  1044. {
  1045. foreach ($member_info as $member)
  1046. {
  1047. $replacements = array(
  1048. 'NAME' => $member['name'],
  1049. 'USERNAME' => $member['username'],
  1050. 'PROFILELINK' => $scripturl . '?action=profile;u=' . $member['id'],
  1051. 'FORGOTPASSWORDLINK' => $scripturl . '?action=reminder',
  1052. );
  1053. $emaildata = loadEmailTemplate('admin_approve_accept', $replacements, $member['language']);
  1054. sendmail($member['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 0);
  1055. }
  1056. }
  1057. }
  1058. // Maybe we're sending it off for activation?
  1059. elseif ($_POST['todo'] == 'require_activation')
  1060. {
  1061. require_once($sourcedir . '/Subs-Members.php');
  1062. // We have to do this for each member I'm afraid.
  1063. foreach ($member_info as $member)
  1064. {
  1065. // Generate a random activation code.
  1066. $validation_code = generateValidationCode();
  1067. // Set these members for activation - I know this includes two id_member checks but it's safer than bodging $condition ;).
  1068. $smcFunc['db_query']('', '
  1069. UPDATE {db_prefix}members
  1070. SET validation_code = {string:validation_code}, is_activated = {int:not_activated}
  1071. WHERE is_activated = {int:activated_status}
  1072. ' . $condition . '
  1073. AND id_member = {int:selected_member}',
  1074. array(
  1075. 'not_activated' => 0,
  1076. 'activated_status' => $current_filter,
  1077. 'selected_member' => $member['id'],
  1078. 'validation_code' => $validation_code,
  1079. 'time_before' => empty($timeBefore) ? 0 : $timeBefore,
  1080. 'members' => empty($members) ? array() : $members,
  1081. )
  1082. );
  1083. $replacements = array(
  1084. 'USERNAME' => $member['name'],
  1085. 'ACTIVATIONLINK' => $scripturl . '?action=activate;u=' . $member['id'] . ';code=' . $validation_code,
  1086. );
  1087. $emaildata = loadEmailTemplate('admin_approve_activation', $replacements, $member['language']);
  1088. sendmail($member['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 0);
  1089. }
  1090. }
  1091. // Are we rejecting them?
  1092. elseif ($_POST['todo'] == 'reject' || $_POST['todo'] == 'rejectemail')
  1093. {
  1094. require_once($sourcedir . '/Subs-Members.php');
  1095. deleteMembers($members);
  1096. // Send email telling them they aren't welcome?
  1097. if ($_POST['todo'] == 'rejectemail')
  1098. {
  1099. foreach ($member_info as $member)
  1100. {
  1101. $replacements = array(
  1102. 'USERNAME' => $member['name'],
  1103. );
  1104. $emaildata = loadEmailTemplate('admin_approve_reject', $replacements, $member['language']);
  1105. sendmail($member['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 1);
  1106. }
  1107. }
  1108. }
  1109. // A simple delete?
  1110. elseif ($_POST['todo'] == 'delete' || $_POST['todo'] == 'deleteemail')
  1111. {
  1112. require_once($sourcedir . '/Subs-Members.php');
  1113. deleteMembers($members);
  1114. // Send email telling them they aren't welcome?
  1115. if ($_POST['todo'] == 'deleteemail')
  1116. {
  1117. foreach ($member_info as $member)
  1118. {
  1119. $replacements = array(
  1120. 'USERNAME' => $member['name'],
  1121. );
  1122. $emaildata = loadEmailTemplate('admin_approve_delete', $replacements, $member['language']);
  1123. sendmail($member['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 1);
  1124. }
  1125. }
  1126. }
  1127. // Remind them to activate their account?
  1128. elseif ($_POST['todo'] == 'remind')
  1129. {
  1130. foreach ($member_info as $member)
  1131. {
  1132. $replacements = array(
  1133. 'USERNAME' => $member['name'],
  1134. 'ACTIVATIONLINK' => $scripturl . '?action=activate;u=' . $member['id'] . ';code=' . $member['code'],
  1135. );
  1136. $emaildata = loadEmailTemplate('admin_approve_remind', $replacements, $member['language']);
  1137. sendmail($member['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 1);
  1138. }
  1139. }
  1140. // Back to the user's language!
  1141. if (isset($current_language) && $current_language != $user_info['language'])
  1142. {
  1143. loadLanguage('index');
  1144. loadLanguage('ManageMembers');
  1145. }
  1146. // Log what we did?
  1147. if (!empty($modSettings['modlog_enabled']) && (substr($_POST['todo'], 0, 2) == 'ok' || $_POST['todo'] == 'remind'))
  1148. {
  1149. $log_action = $_POST['todo'] == 'remind' ? 'remind_member' : 'approve_member';
  1150. $log_inserts = array();
  1151. foreach ($member_info as $member)
  1152. {
  1153. $log_inserts[] = array(
  1154. time(), 3, $user_info['id'], $user_info['ip'], $log_action,
  1155. 0, 0, 0, serialize(array('member' => $member['id'])),
  1156. );
  1157. }
  1158. $smcFunc['db_insert']('',
  1159. '{db_prefix}log_actions',
  1160. array(
  1161. 'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'string-16', 'action' => 'string',
  1162. 'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
  1163. ),
  1164. $log_inserts,
  1165. array('id_action')
  1166. );
  1167. }
  1168. // Although updateStats *may* catch this, best to do it manually just in case (Doesn't always sort out unapprovedMembers).
  1169. if (in_array($current_filter, array(3, 4)))
  1170. updateSettings(array('unapprovedMembers' => ($modSettings['unapprovedMembers'] > $member_count ? $modSettings['unapprovedMembers'] - $member_count : 0)));
  1171. // Update the member's stats. (but, we know the member didn't change their name.)
  1172. updateStats('member', false);
  1173. // If they haven't been deleted, update the post group statistics on them...
  1174. if (!in_array($_POST['todo'], array('delete', 'deleteemail', 'reject', 'rejectemail', 'remind')))
  1175. updateStats('postgroups', $members);
  1176. redirectexit('action=admin;area=viewmembers;sa=browse;type=' . $_REQUEST['type'] . ';sort=' . $_REQUEST['sort'] . ';filter=' . $current_filter . ';start=' . $_REQUEST['start']);
  1177. }
  1178. function jeffsdatediff($old)
  1179. {
  1180. // Get the current time as the user would see it...
  1181. $forumTime = forum_time();
  1182. // Calculate the seconds that have passed since midnight.
  1183. $sinceMidnight = date('H', $forumTime) * 60 * 60 + date('i', $forumTime) * 60 + date('s', $forumTime);
  1184. // Take the difference between the two times.
  1185. $dis = time() - $old;
  1186. // Before midnight?
  1187. if ($dis < $sinceMidnight)
  1188. return 0;
  1189. else
  1190. $dis -= $sinceMidnight;
  1191. // Divide out the seconds in a day to get the number of days.
  1192. return ceil($dis / (24 * 60 * 60));
  1193. }
  1194. ?>