Reports.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940
  1. <?php
  2. /**
  3. * Simple Machines Forum (SMF)
  4. *
  5. * @package SMF
  6. * @author Simple Machines http://www.simplemachines.org
  7. * @copyright 2011 Simple Machines
  8. * @license http://www.simplemachines.org/about/smf/license.php BSD
  9. *
  10. * @version 2.0
  11. */
  12. if (!defined('SMF'))
  13. die('Hacking attempt...');
  14. /* This file is exclusively for generating reports to help assist forum
  15. administrators keep track of their forum configuration and state. The
  16. core report generation is done in two areas. Firstly, a report "generator"
  17. will fill context with relevant data. Secondly, the choice of sub-template
  18. will determine how this data is shown to the user. It has the following
  19. functions:
  20. void ReportsMain()
  21. - requires the admin_forum permission.
  22. - loads the Reports template and language files.
  23. - decides which type of report to generate, if this isn't passed
  24. through the querystring it will set the report_type sub-template to
  25. force the user to choose which type.
  26. - when generating a report chooses which sub_template to use.
  27. - depends on the cal_enabled setting, and many of the other cal_
  28. settings.
  29. - will call the relevant report generation function.
  30. - if generating report will call finishTables before returning.
  31. - accessed through ?action=admin;area=reports.
  32. void xxxxxxReport()
  33. - functions ending with "Report" are responsible for generating data
  34. for reporting.
  35. - they are all called from ReportsMain.
  36. - never access the context directly, but use the data handling
  37. functions to do so.
  38. void newTable(string title = '', string default_value = '',
  39. string shading = 'all', string width_normal = 'auto',
  40. string align_normal = 'center', string width_shaded = 'auto',
  41. string align_shaded = 'auto')
  42. - the core of this file, it creates a new, but empty, table of data in
  43. context, ready for filling using addData().
  44. - takes a lot of possible attributes, these have the following effect:
  45. + title = Title to be displayed with this data table.
  46. + default_value = Value to be displayed if a key is missing from a
  47. row.
  48. + shading = Should the left, top or both (all) parts of the table
  49. beshaded?
  50. + width_normal = width of an unshaded column (auto means not
  51. defined).
  52. + align_normal = alignment of data in an unshaded column.
  53. + width_shaded = width of a shaded column (auto means not
  54. defined).
  55. + align_shaded = alignment of data in a shaded column.
  56. - fills the context variable current_table with the ID of the table
  57. created.
  58. - keeps track of the current table count using context variable
  59. table_count.
  60. void addData(array inc_data, int custom_table = null)
  61. - adds an array of data into an existing table.
  62. - if there are no existing tables, will create one with default
  63. attributes.
  64. - if custom_table isn't specified, it will use the last table created,
  65. if it is specified and doesn't exist the function will return false.
  66. - if a set of keys have been specified, the function will check each
  67. required key is present in the incoming data. If this data is missing
  68. the current tables default value will be used.
  69. - if any key in the incoming data begins with '#sep#', the function
  70. will add a separator accross the table at this point.
  71. - once the incoming data has been sanitized, it is added to the table.
  72. void addSeparator(string title = '', int custom_table = null)
  73. - adds a separator with title given by attribute "title" after the
  74. current row in the table.
  75. - if there are no existing tables, will create one with default
  76. attributes.
  77. - if custom_table isn't specified, it will use the last table created,
  78. if it is specified and doesn't exist the function will return false.
  79. - if the table is currently having data added by column this may have
  80. unpredictable visual results.
  81. void finishTables()
  82. - is (unfortunately) required to create some useful variables for
  83. templates.
  84. - foreach data table created, it will count the number of rows and
  85. columns in the table.
  86. - will also create a max_width variable for the table, to give an
  87. estimate width for the whole table - if it can.
  88. void setKeys(string method = 'rows', array keys = array(),
  89. bool reverse = false)
  90. - sets the current set of "keys" expected in each data array passed to
  91. addData. It also sets the way we are adding data to the data table.
  92. - method specifies whether the data passed to addData represents a new
  93. column, or a new row.
  94. - keys is an array whose keys are the keys for data being passed to
  95. addData().
  96. - if reverse is set to true, then the values of the variable "keys"
  97. are used as oppossed to the keys(!)
  98. */
  99. // Handling function for generating reports.
  100. function ReportsMain()
  101. {
  102. global $txt, $modSettings, $context, $scripturl;
  103. // Only admins, only EVER admins!
  104. isAllowedTo('admin_forum');
  105. // Let's get our things running...
  106. loadTemplate('Reports');
  107. loadLanguage('Reports');
  108. $context['page_title'] = $txt['generate_reports'];
  109. // These are the types of reports which exist - and the functions to generate them.
  110. $context['report_types'] = array(
  111. 'boards' => 'BoardReport',
  112. 'board_perms' => 'BoardPermissionsReport',
  113. 'member_groups' => 'MemberGroupsReport',
  114. 'group_perms' => 'GroupPermissionsReport',
  115. 'staff' => 'StaffReport',
  116. );
  117. $is_first = 0;
  118. foreach ($context['report_types'] as $k => $temp)
  119. $context['report_types'][$k] = array(
  120. 'id' => $k,
  121. 'title' => isset($txt['gr_type_' . $k]) ? $txt['gr_type_' . $k] : $type['id'],
  122. 'description' => isset($txt['gr_type_desc_' . $k]) ? $txt['gr_type_desc_' . $k] : null,
  123. 'function' => $temp,
  124. 'is_first' => $is_first++ == 0,
  125. );
  126. // If they haven't choosen a report type which is valid, send them off to the report type chooser!
  127. if (empty($_REQUEST['rt']) || !isset($context['report_types'][$_REQUEST['rt']]))
  128. {
  129. $context['sub_template'] = 'report_type';
  130. return;
  131. }
  132. $context['report_type'] = $_REQUEST['rt'];
  133. // What are valid templates for showing reports?
  134. $reportTemplates = array(
  135. 'main' => array(
  136. 'layers' => null,
  137. ),
  138. 'print' => array(
  139. 'layers' => array('print'),
  140. ),
  141. );
  142. // Specific template? Use that instead of main!
  143. if (isset($_REQUEST['st']) && isset($reportTemplates[$_REQUEST['st']]))
  144. {
  145. $context['sub_template'] = $_REQUEST['st'];
  146. // Are we disabling the other layers - print friendly for example?
  147. if ($reportTemplates[$_REQUEST['st']]['layers'] !== null)
  148. $context['template_layers'] = $reportTemplates[$_REQUEST['st']]['layers'];
  149. }
  150. // Make the page title more descriptive.
  151. $context['page_title'] .= ' - ' . (isset($txt['gr_type_' . $context['report_type']]) ? $txt['gr_type_' . $context['report_type']] : $context['report_type']);
  152. // Now generate the data.
  153. $context['report_types'][$context['report_type']]['function']();
  154. // Finish the tables before exiting - this is to help the templates a little more.
  155. finishTables();
  156. }
  157. // Standard report about what settings the boards have.
  158. function BoardReport()
  159. {
  160. global $context, $txt, $sourcedir, $smcFunc;
  161. // Load the permission profiles.
  162. require_once($sourcedir . '/ManagePermissions.php');
  163. loadLanguage('ManagePermissions');
  164. loadPermissionProfiles();
  165. // Get every moderator.
  166. $request = $smcFunc['db_query']('', '
  167. SELECT mods.id_board, mods.id_member, mem.real_name
  168. FROM {db_prefix}moderators AS mods
  169. INNER JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)',
  170. array(
  171. )
  172. );
  173. $moderators = array();
  174. while ($row = $smcFunc['db_fetch_assoc']($request))
  175. $moderators[$row['id_board']][] = $row['real_name'];
  176. $smcFunc['db_free_result']($request);
  177. // Get all the possible membergroups!
  178. $request = $smcFunc['db_query']('', '
  179. SELECT id_group, group_name, online_color
  180. FROM {db_prefix}membergroups',
  181. array(
  182. )
  183. );
  184. $groups = array(-1 => $txt['guest_title'], 0 => $txt['full_member']);
  185. while ($row = $smcFunc['db_fetch_assoc']($request))
  186. $groups[$row['id_group']] = empty($row['online_color']) ? $row['group_name'] : '<span style="color: ' . $row['online_color'] . '">' . $row['group_name'] . '</span>';
  187. $smcFunc['db_free_result']($request);
  188. // All the fields we'll show.
  189. $boardSettings = array(
  190. 'category' => $txt['board_category'],
  191. 'parent' => $txt['board_parent'],
  192. 'num_topics' => $txt['board_num_topics'],
  193. 'num_posts' => $txt['board_num_posts'],
  194. 'count_posts' => $txt['board_count_posts'],
  195. 'theme' => $txt['board_theme'],
  196. 'override_theme' => $txt['board_override_theme'],
  197. 'profile' => $txt['board_profile'],
  198. 'moderators' => $txt['board_moderators'],
  199. 'groups' => $txt['board_groups'],
  200. );
  201. // Do it in columns, it's just easier.
  202. setKeys('cols');
  203. // Go through each board!
  204. $request = $smcFunc['db_query']('order_by_board_order', '
  205. SELECT b.id_board, b.name, b.num_posts, b.num_topics, b.count_posts, b.member_groups, b.override_theme, b.id_profile,
  206. c.name AS cat_name, IFNULL(par.name, {string:text_none}) AS parent_name, IFNULL(th.value, {string:text_none}) AS theme_name
  207. FROM {db_prefix}boards AS b
  208. LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
  209. LEFT JOIN {db_prefix}boards AS par ON (par.id_board = b.id_parent)
  210. LEFT JOIN {db_prefix}themes AS th ON (th.id_theme = b.id_theme AND th.variable = {string:name})',
  211. array(
  212. 'name' => 'name',
  213. 'text_none' => $txt['none'],
  214. )
  215. );
  216. $boards = array(0 => array('name' => $txt['global_boards']));
  217. while ($row = $smcFunc['db_fetch_assoc']($request))
  218. {
  219. // Each board has it's own table.
  220. newTable($row['name'], '', 'left', 'auto', 'left', 200, 'left');
  221. // First off, add in the side key.
  222. addData($boardSettings);
  223. // Format the profile name.
  224. $profile_name = $context['profiles'][$row['id_profile']]['name'];
  225. // Create the main data array.
  226. $boardData = array(
  227. 'category' => $row['cat_name'],
  228. 'parent' => $row['parent_name'],
  229. 'num_posts' => $row['num_posts'],
  230. 'num_topics' => $row['num_topics'],
  231. 'count_posts' => empty($row['count_posts']) ? $txt['yes'] : $txt['no'],
  232. 'theme' => $row['theme_name'],
  233. 'profile' => $profile_name,
  234. 'override_theme' => $row['override_theme'] ? $txt['yes'] : $txt['no'],
  235. 'moderators' => empty($moderators[$row['id_board']]) ? $txt['none'] : implode(', ', $moderators[$row['id_board']]),
  236. );
  237. // Work out the membergroups who can access it.
  238. $allowedGroups = explode(',', $row['member_groups']);
  239. foreach ($allowedGroups as $key => $group)
  240. {
  241. if (isset($groups[$group]))
  242. $allowedGroups[$key] = $groups[$group];
  243. else
  244. unset($allowedGroups[$key]);
  245. }
  246. $boardData['groups'] = implode(', ', $allowedGroups);
  247. // Next add the main data.
  248. addData($boardData);
  249. }
  250. $smcFunc['db_free_result']($request);
  251. }
  252. // Generate a report on the current permissions by board and membergroup.
  253. function BoardPermissionsReport()
  254. {
  255. global $context, $txt, $modSettings, $smcFunc;
  256. // Get as much memory as possible as this can be big.
  257. @ini_set('memory_limit', '256M');
  258. if (isset($_REQUEST['boards']))
  259. {
  260. if (!is_array($_REQUEST['boards']))
  261. $_REQUEST['boards'] = explode(',', $_REQUEST['boards']);
  262. foreach ($_REQUEST['boards'] as $k => $dummy)
  263. $_REQUEST['boards'][$k] = (int) $dummy;
  264. $board_clause = 'id_board IN ({array_int:boards})';
  265. }
  266. else
  267. $board_clause = '1=1';
  268. if (isset($_REQUEST['groups']))
  269. {
  270. if (!is_array($_REQUEST['groups']))
  271. $_REQUEST['groups'] = explode(',', $_REQUEST['groups']);
  272. foreach ($_REQUEST['groups'] as $k => $dummy)
  273. $_REQUEST['groups'][$k] = (int) $dummy;
  274. $group_clause = 'id_group IN ({array_int:groups})';
  275. }
  276. else
  277. $group_clause = '1=1';
  278. // Fetch all the board names.
  279. $request = $smcFunc['db_query']('', '
  280. SELECT id_board, name, id_profile
  281. FROM {db_prefix}boards
  282. WHERE ' . $board_clause . '
  283. ORDER BY id_board',
  284. array(
  285. 'boards' => isset($_REQUEST['boards']) ? $_REQUEST['boards'] : array(),
  286. )
  287. );
  288. $profiles = array();
  289. while ($row = $smcFunc['db_fetch_assoc']($request))
  290. {
  291. $boards[$row['id_board']] = array(
  292. 'name' => $row['name'],
  293. 'profile' => $row['id_profile'],
  294. );
  295. $profiles[] = $row['id_profile'];
  296. }
  297. $smcFunc['db_free_result']($request);
  298. // Get all the possible membergroups, except admin!
  299. $request = $smcFunc['db_query']('', '
  300. SELECT id_group, group_name
  301. FROM {db_prefix}membergroups
  302. WHERE ' . $group_clause . '
  303. AND id_group != {int:admin_group}' . (empty($modSettings['permission_enable_postgroups']) ? '
  304. AND min_posts = {int:min_posts}' : '') . '
  305. ORDER BY min_posts, CASE WHEN id_group < {int:newbie_group} THEN id_group ELSE 4 END, group_name',
  306. array(
  307. 'admin_group' => 1,
  308. 'min_posts' => -1,
  309. 'newbie_group' => 4,
  310. 'groups' => isset($_REQUEST['groups']) ? $_REQUEST['groups'] : array(),
  311. )
  312. );
  313. if (!isset($_REQUEST['groups']) || in_array(-1, $_REQUEST['groups']) || in_array(0, $_REQUEST['groups']))
  314. $member_groups = array('col' => '', -1 => $txt['membergroups_guests'], 0 => $txt['membergroups_members']);
  315. else
  316. $member_groups = array('col' => '');
  317. while ($row = $smcFunc['db_fetch_assoc']($request))
  318. $member_groups[$row['id_group']] = $row['group_name'];
  319. $smcFunc['db_free_result']($request);
  320. // Make sure that every group is represented - plus in rows!
  321. setKeys('rows', $member_groups);
  322. // Cache every permission setting, to make sure we don't miss any allows.
  323. $permissions = array();
  324. $board_permissions = array();
  325. $request = $smcFunc['db_query']('', '
  326. SELECT id_profile, id_group, add_deny, permission
  327. FROM {db_prefix}board_permissions
  328. WHERE id_profile IN ({array_int:profile_list})
  329. AND ' . $group_clause . (empty($modSettings['permission_enable_deny']) ? '
  330. AND add_deny = {int:not_deny}' : '') . '
  331. ORDER BY id_profile, permission',
  332. array(
  333. 'profile_list' => $profiles,
  334. 'not_deny' => 1,
  335. 'groups' => isset($_REQUEST['groups']) ? $_REQUEST['groups'] : array(),
  336. )
  337. );
  338. while ($row = $smcFunc['db_fetch_assoc']($request))
  339. {
  340. foreach ($boards as $id => $board)
  341. if ($board['profile'] == $row['id_profile'])
  342. $board_permissions[$id][$row['id_group']][$row['permission']] = $row['add_deny'];
  343. // Make sure we get every permission.
  344. if (!isset($permissions[$row['permission']]))
  345. {
  346. // This will be reused on other boards.
  347. $permissions[$row['permission']] = array(
  348. 'title' => isset($txt['board_perms_name_' . $row['permission']]) ? $txt['board_perms_name_' . $row['permission']] : $row['permission'],
  349. );
  350. }
  351. }
  352. $smcFunc['db_free_result']($request);
  353. // Now cycle through the board permissions array... lots to do ;)
  354. foreach ($board_permissions as $board => $groups)
  355. {
  356. // Create the table for this board first.
  357. newTable($boards[$board]['name'], 'x', 'all', 100, 'center', 200, 'left');
  358. // Add the header row - shows all the membergroups.
  359. addData($member_groups);
  360. // Add the separator.
  361. addSeparator($txt['board_perms_permission']);
  362. // Here cycle through all the detected permissions.
  363. foreach ($permissions as $ID_PERM => $perm_info)
  364. {
  365. // Is this identical to the global?
  366. $identicalGlobal = $board == 0 ? false : true;
  367. // Default data for this row.
  368. $curData = array('col' => $perm_info['title']);
  369. // Now cycle each membergroup in this set of permissions.
  370. foreach ($member_groups as $id_group => $name)
  371. {
  372. // Don't overwrite the key column!
  373. if ($id_group === 'col')
  374. continue;
  375. $group_permissions = isset($groups[$id_group]) ? $groups[$id_group] : array();
  376. // Do we have any data for this group?
  377. if (isset($group_permissions[$ID_PERM]))
  378. {
  379. // Set the data for this group to be the local permission.
  380. $curData[$id_group] = $group_permissions[$ID_PERM];
  381. }
  382. // Otherwise means it's set to disallow..
  383. else
  384. {
  385. $curData[$id_group] = 'x';
  386. }
  387. // Now actually make the data for the group look right.
  388. if (empty($curData[$id_group]))
  389. $curData[$id_group] = '<span style="color: red;">' . $txt['board_perms_deny'] . '</span>';
  390. elseif ($curData[$id_group] == 1)
  391. $curData[$id_group] = '<span style="color: darkgreen;">' . $txt['board_perms_allow'] . '</span>';
  392. else
  393. $curData[$id_group] = 'x';
  394. // Embolden those permissions different from global (makes it a lot easier!)
  395. if (@$board_permissions[0][$id_group][$ID_PERM] != @$group_permissions[$ID_PERM])
  396. $curData[$id_group] = '<strong>' . $curData[$id_group] . '</strong>';
  397. }
  398. // Now add the data for this permission.
  399. addData($curData);
  400. }
  401. }
  402. }
  403. // Show what the membergroups are made of.
  404. function MemberGroupsReport()
  405. {
  406. global $context, $txt, $settings, $modSettings, $smcFunc;
  407. // Fetch all the board names.
  408. $request = $smcFunc['db_query']('', '
  409. SELECT id_board, name, member_groups, id_profile
  410. FROM {db_prefix}boards',
  411. array(
  412. )
  413. );
  414. while ($row = $smcFunc['db_fetch_assoc']($request))
  415. {
  416. if (trim($row['member_groups']) == '')
  417. $groups = array(1);
  418. else
  419. $groups = array_merge(array(1), explode(',', $row['member_groups']));
  420. $boards[$row['id_board']] = array(
  421. 'id' => $row['id_board'],
  422. 'name' => $row['name'],
  423. 'profile' => $row['id_profile'],
  424. 'groups' => $groups,
  425. );
  426. }
  427. $smcFunc['db_free_result']($request);
  428. // Standard settings.
  429. $mgSettings = array(
  430. 'name' => '',
  431. '#sep#1' => $txt['member_group_settings'],
  432. 'color' => $txt['member_group_color'],
  433. 'min_posts' => $txt['member_group_min_posts'],
  434. 'max_messages' => $txt['member_group_max_messages'],
  435. 'stars' => $txt['member_group_stars'],
  436. '#sep#2' => $txt['member_group_access'],
  437. );
  438. // Add on the boards!
  439. foreach ($boards as $board)
  440. $mgSettings['board_' . $board['id']] = $board['name'];
  441. // Add all the membergroup settings, plus we'll be adding in columns!
  442. setKeys('cols', $mgSettings);
  443. // Only one table this time!
  444. newTable($txt['gr_type_member_groups'], '-', 'all', 100, 'center', 200, 'left');
  445. // Get the shaded column in.
  446. addData($mgSettings);
  447. // Now start cycling the membergroups!
  448. $request = $smcFunc['db_query']('', '
  449. SELECT mg.id_group, mg.group_name, mg.online_color, mg.min_posts, mg.max_messages, mg.stars,
  450. CASE WHEN bp.permission IS NOT NULL OR mg.id_group = {int:admin_group} THEN 1 ELSE 0 END AS can_moderate
  451. FROM {db_prefix}membergroups AS mg
  452. LEFT JOIN {db_prefix}board_permissions AS bp ON (bp.id_group = mg.id_group AND bp.id_profile = {int:default_profile} AND bp.permission = {string:moderate_board})
  453. ORDER BY mg.min_posts, CASE WHEN mg.id_group < {int:newbie_group} THEN mg.id_group ELSE 4 END, mg.group_name',
  454. array(
  455. 'admin_group' => 1,
  456. 'default_profile' => 1,
  457. 'newbie_group' => 4,
  458. 'moderate_board' => 'moderate_board',
  459. )
  460. );
  461. // Cache them so we get regular members too.
  462. $rows = array(
  463. array(
  464. 'id_group' => -1,
  465. 'group_name' => $txt['membergroups_guests'],
  466. 'online_color' => '',
  467. 'min_posts' => -1,
  468. 'max_messages' => null,
  469. 'stars' => ''
  470. ),
  471. array(
  472. 'id_group' => 0,
  473. 'group_name' => $txt['membergroups_members'],
  474. 'online_color' => '',
  475. 'min_posts' => -1,
  476. 'max_messages' => null,
  477. 'stars' => ''
  478. ),
  479. );
  480. while ($row = $smcFunc['db_fetch_assoc']($request))
  481. $rows[] = $row;
  482. $smcFunc['db_free_result']($request);
  483. foreach ($rows as $row)
  484. {
  485. $row['stars'] = explode('#', $row['stars']);
  486. $group = array(
  487. 'name' => $row['group_name'],
  488. 'color' => empty($row['online_color']) ? '-' : '<span style="color: ' . $row['online_color'] . ';">' . $row['online_color'] . '</span>',
  489. 'min_posts' => $row['min_posts'] == -1 ? 'N/A' : $row['min_posts'],
  490. 'max_messages' => $row['max_messages'],
  491. 'stars' => !empty($row['stars'][0]) && !empty($row['stars'][1]) ? str_repeat('<img src="' . $settings['images_url'] . '/' . $row['stars'][1] . '" alt="*" />', $row['stars'][0]) : '',
  492. );
  493. // Board permissions.
  494. foreach ($boards as $board)
  495. $group['board_' . $board['id']] = in_array($row['id_group'], $board['groups']) ? '<span style="color: darkgreen;">' . $txt['board_perms_allow'] . '</span>' : 'x';
  496. addData($group);
  497. }
  498. }
  499. // Show the large variety of group permissions assigned to each membergroup.
  500. function GroupPermissionsReport()
  501. {
  502. global $context, $txt, $modSettings, $smcFunc;
  503. if (isset($_REQUEST['groups']))
  504. {
  505. if (!is_array($_REQUEST['groups']))
  506. $_REQUEST['groups'] = explode(',', $_REQUEST['groups']);
  507. foreach ($_REQUEST['groups'] as $k => $dummy)
  508. $_REQUEST['groups'][$k] = (int) $dummy;
  509. $_REQUEST['groups'] = array_diff($_REQUEST['groups'], array(3));
  510. $clause = 'id_group IN ({array_int:groups})';
  511. }
  512. else
  513. $clause = 'id_group != {int:moderator_group}';
  514. // Get all the possible membergroups, except admin!
  515. $request = $smcFunc['db_query']('', '
  516. SELECT id_group, group_name
  517. FROM {db_prefix}membergroups
  518. WHERE ' . $clause . '
  519. AND id_group != {int:admin_group}' . (empty($modSettings['permission_enable_postgroups']) ? '
  520. AND min_posts = {int:min_posts}' : '') . '
  521. ORDER BY min_posts, CASE WHEN id_group < {int:newbie_group} THEN id_group ELSE 4 END, group_name',
  522. array(
  523. 'admin_group' => 1,
  524. 'min_posts' => -1,
  525. 'newbie_group' => 4,
  526. 'moderator_group' => 3,
  527. 'groups' => isset($_REQUEST['groups']) ? $_REQUEST['groups'] : array(),
  528. )
  529. );
  530. if (!isset($_REQUEST['groups']) || in_array(-1, $_REQUEST['groups']) || in_array(0, $_REQUEST['groups']))
  531. $groups = array('col' => '', -1 => $txt['membergroups_guests'], 0 => $txt['membergroups_members']);
  532. else
  533. $groups = array('col' => '');
  534. while ($row = $smcFunc['db_fetch_assoc']($request))
  535. $groups[$row['id_group']] = $row['group_name'];
  536. $smcFunc['db_free_result']($request);
  537. // Make sure that every group is represented!
  538. setKeys('rows', $groups);
  539. // Create the table first.
  540. newTable($txt['gr_type_group_perms'], '-', 'all', 100, 'center', 200, 'left');
  541. // Show all the groups
  542. addData($groups);
  543. // Add a separator
  544. addSeparator($txt['board_perms_permission']);
  545. // Now the big permission fetch!
  546. $request = $smcFunc['db_query']('', '
  547. SELECT id_group, add_deny, permission
  548. FROM {db_prefix}permissions
  549. WHERE ' . $clause . (empty($modSettings['permission_enable_deny']) ? '
  550. AND add_deny = {int:not_denied}' : '') . '
  551. ORDER BY permission',
  552. array(
  553. 'not_denied' => 1,
  554. 'moderator_group' => 3,
  555. 'groups' => isset($_REQUEST['groups']) ? $_REQUEST['groups'] : array(),
  556. )
  557. );
  558. $lastPermission = null;
  559. while ($row = $smcFunc['db_fetch_assoc']($request))
  560. {
  561. // If this is a new permission flush the last row.
  562. if ($row['permission'] != $lastPermission)
  563. {
  564. // Send the data!
  565. if ($lastPermission !== null)
  566. addData($curData);
  567. // Add the permission name in the left column.
  568. $curData = array('col' => isset($txt['group_perms_name_' . $row['permission']]) ? $txt['group_perms_name_' . $row['permission']] : $row['permission']);
  569. $lastPermission = $row['permission'];
  570. }
  571. // Good stuff - add the permission to the list!
  572. if ($row['add_deny'])
  573. $curData[$row['id_group']] = '<span style="color: darkgreen;">' . $txt['board_perms_allow'] . '</span>';
  574. else
  575. $curData[$row['id_group']] = '<span style="color: red;">' . $txt['board_perms_deny'] . '</span>';
  576. }
  577. $smcFunc['db_free_result']($request);
  578. // Flush the last data!
  579. addData($curData);
  580. }
  581. // Report for showing all the forum staff members - quite a feat!
  582. function StaffReport()
  583. {
  584. global $sourcedir, $context, $txt, $smcFunc;
  585. require_once($sourcedir . '/Subs-Members.php');
  586. // Fetch all the board names.
  587. $request = $smcFunc['db_query']('', '
  588. SELECT id_board, name
  589. FROM {db_prefix}boards',
  590. array(
  591. )
  592. );
  593. $boards = array();
  594. while ($row = $smcFunc['db_fetch_assoc']($request))
  595. $boards[$row['id_board']] = $row['name'];
  596. $smcFunc['db_free_result']($request);
  597. // Get every moderator.
  598. $request = $smcFunc['db_query']('', '
  599. SELECT mods.id_board, mods.id_member
  600. FROM {db_prefix}moderators AS mods',
  601. array(
  602. )
  603. );
  604. $moderators = array();
  605. $local_mods = array();
  606. while ($row = $smcFunc['db_fetch_assoc']($request))
  607. {
  608. $moderators[$row['id_member']][] = $row['id_board'];
  609. $local_mods[$row['id_member']] = $row['id_member'];
  610. }
  611. $smcFunc['db_free_result']($request);
  612. // Get a list of global moderators (i.e. members with moderation powers).
  613. $global_mods = array_intersect(membersAllowedTo('moderate_board', 0), membersAllowedTo('approve_posts', 0), membersAllowedTo('remove_any', 0), membersAllowedTo('modify_any', 0));
  614. // How about anyone else who is special?
  615. $allStaff = array_merge(membersAllowedTo('admin_forum'), membersAllowedTo('manage_membergroups'), membersAllowedTo('manage_permissions'), $local_mods, $global_mods);
  616. // Make sure everyone is there once - no admin less important than any other!
  617. $allStaff = array_unique($allStaff);
  618. // This is a bit of a cop out - but we're protecting their forum, really!
  619. if (count($allStaff) > 300)
  620. fatal_lang_error('report_error_too_many_staff');
  621. // Get all the possible membergroups!
  622. $request = $smcFunc['db_query']('', '
  623. SELECT id_group, group_name, online_color
  624. FROM {db_prefix}membergroups',
  625. array(
  626. )
  627. );
  628. $groups = array(0 => $txt['full_member']);
  629. while ($row = $smcFunc['db_fetch_assoc']($request))
  630. $groups[$row['id_group']] = empty($row['online_color']) ? $row['group_name'] : '<span style="color: ' . $row['online_color'] . '">' . $row['group_name'] . '</span>';
  631. $smcFunc['db_free_result']($request);
  632. // All the fields we'll show.
  633. $staffSettings = array(
  634. 'position' => $txt['report_staff_position'],
  635. 'moderates' => $txt['report_staff_moderates'],
  636. 'posts' => $txt['report_staff_posts'],
  637. 'last_login' => $txt['report_staff_last_login'],
  638. );
  639. // Do it in columns, it's just easier.
  640. setKeys('cols');
  641. // Get each member!
  642. $request = $smcFunc['db_query']('', '
  643. SELECT id_member, real_name, id_group, posts, last_login
  644. FROM {db_prefix}members
  645. WHERE id_member IN ({array_int:staff_list})
  646. ORDER BY real_name',
  647. array(
  648. 'staff_list' => $allStaff,
  649. )
  650. );
  651. while ($row = $smcFunc['db_fetch_assoc']($request))
  652. {
  653. // Each member gets their own table!.
  654. newTable($row['real_name'], '', 'left', 'auto', 'left', 200, 'center');
  655. // First off, add in the side key.
  656. addData($staffSettings);
  657. // Create the main data array.
  658. $staffData = array(
  659. 'position' => isset($groups[$row['id_group']]) ? $groups[$row['id_group']] : $groups[0],
  660. 'posts' => $row['posts'],
  661. 'last_login' => timeformat($row['last_login']),
  662. 'moderates' => array(),
  663. );
  664. // What do they moderate?
  665. if (in_array($row['id_member'], $global_mods))
  666. $staffData['moderates'] = '<em>' . $txt['report_staff_all_boards'] . '</em>';
  667. elseif (isset($moderators[$row['id_member']]))
  668. {
  669. // Get the names
  670. foreach ($moderators[$row['id_member']] as $board)
  671. if (isset($boards[$board]))
  672. $staffData['moderates'][] = $boards[$board];
  673. $staffData['moderates'] = implode(', ', $staffData['moderates']);
  674. }
  675. else
  676. $staffData['moderates'] = '<em>' . $txt['report_staff_no_boards'] . '</em>';
  677. // Next add the main data.
  678. addData($staffData);
  679. }
  680. $smcFunc['db_free_result']($request);
  681. }
  682. // This function creates a new table of data, most functions will only use it once.
  683. function newTable($title = '', $default_value = '', $shading = 'all', $width_normal = 'auto', $align_normal = 'center', $width_shaded = 'auto', $align_shaded = 'auto')
  684. {
  685. global $context;
  686. // Set the table count if needed.
  687. if (empty($context['table_count']))
  688. $context['table_count'] = 0;
  689. // Create the table!
  690. $context['tables'][$context['table_count']] = array(
  691. 'title' => $title,
  692. 'default_value' => $default_value,
  693. 'shading' => array(
  694. 'left' => $shading == 'all' || $shading == 'left',
  695. 'top' => $shading == 'all' || $shading == 'top',
  696. ),
  697. 'width' => array(
  698. 'normal' => $width_normal,
  699. 'shaded' => $width_shaded,
  700. ),
  701. 'align' => array(
  702. 'normal' => $align_normal,
  703. 'shaded' => $align_shaded,
  704. ),
  705. 'data' => array(),
  706. );
  707. $context['current_table'] = $context['table_count'];
  708. // Increment the count...
  709. $context['table_count']++;
  710. }
  711. // Add an extra slice of data to the table
  712. function addData($inc_data, $custom_table = null)
  713. {
  714. global $context;
  715. // No tables? Create one even though we are probably already in a bad state!
  716. if (empty($context['table_count']))
  717. newTable();
  718. // Specific table?
  719. if ($custom_table !== null && !isset($context['tables'][$custom_table]))
  720. return false;
  721. elseif ($custom_table !== null)
  722. $table = $custom_table;
  723. else
  724. $table = $context['current_table'];
  725. // If we have keys, sanitise the data...
  726. if (!empty($context['keys']))
  727. {
  728. // Basically, check every key exists!
  729. foreach ($context['keys'] as $key => $dummy)
  730. {
  731. $data[$key] = array(
  732. 'v' => empty($inc_data[$key]) ? $context['tables'][$table]['default_value'] : $inc_data[$key],
  733. );
  734. // Special "hack" the adding separators when doing data by column.
  735. if (substr($key, 0, 5) == '#sep#')
  736. $data[$key]['separator'] = true;
  737. }
  738. }
  739. else
  740. {
  741. $data = $inc_data;
  742. foreach ($data as $key => $value)
  743. {
  744. $data[$key] = array(
  745. 'v' => $value,
  746. );
  747. if (substr($key, 0, 5) == '#sep#')
  748. $data[$key]['separator'] = true;
  749. }
  750. }
  751. // Is it by row?
  752. if (empty($context['key_method']) || $context['key_method'] == 'rows')
  753. {
  754. // Add the data!
  755. $context['tables'][$table]['data'][] = $data;
  756. }
  757. // Otherwise, tricky!
  758. else
  759. {
  760. foreach ($data as $key => $item)
  761. $context['tables'][$table]['data'][$key][] = $item;
  762. }
  763. }
  764. // Add a separator row, only really used when adding data by rows.
  765. function addSeparator($title = '', $custom_table = null)
  766. {
  767. global $context;
  768. // No tables - return?
  769. if (empty($context['table_count']))
  770. return;
  771. // Specific table?
  772. if ($custom_table !== null && !isset($context['tables'][$table]))
  773. return false;
  774. elseif ($custom_table !== null)
  775. $table = $custom_table;
  776. else
  777. $table = $context['current_table'];
  778. // Plumb in the separator
  779. $context['tables'][$table]['data'][] = array(0 => array(
  780. 'separator' => true,
  781. 'v' => $title
  782. ));
  783. }
  784. // This does the necessary count of table data before displaying them.
  785. function finishTables()
  786. {
  787. global $context;
  788. if (empty($context['tables']))
  789. return;
  790. // Loop through each table counting up some basic values, to help with the templating.
  791. foreach ($context['tables'] as $id => $table)
  792. {
  793. $context['tables'][$id]['id'] = $id;
  794. $context['tables'][$id]['row_count'] = count($table['data']);
  795. $curElement = current($table['data']);
  796. $context['tables'][$id]['column_count'] = count($curElement);
  797. // Work out the rough width - for templates like the print template. Without this we might get funny tables.
  798. if ($table['shading']['left'] && $table['width']['shaded'] != 'auto' && $table['width']['normal'] != 'auto')
  799. $context['tables'][$id]['max_width'] = $table['width']['shaded'] + ($context['tables'][$id]['column_count'] - 1) * $table['width']['normal'];
  800. elseif ($table['width']['normal'] != 'auto')
  801. $context['tables'][$id]['max_width'] = $context['tables'][$id]['column_count'] * $table['width']['normal'];
  802. else
  803. $context['tables'][$id]['max_width'] = 'auto';
  804. }
  805. }
  806. // Set the keys in use by the tables - these ensure entries MUST exist if the data isn't sent.
  807. function setKeys($method = 'rows', $keys = array(), $reverse = false)
  808. {
  809. global $context;
  810. // Do we want to use the keys of the keys as the keys? :P
  811. if ($reverse)
  812. $context['keys'] = array_flip($keys);
  813. else
  814. $context['keys'] = $keys;
  815. // Rows or columns?
  816. $context['key_method'] = $method == 'rows' ? 'rows' : 'cols';
  817. }
  818. ?>