ModerationCenter.php 70 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289
  1. <?php
  2. /**
  3. * Moderation Center.
  4. *
  5. * Simple Machines Forum (SMF)
  6. *
  7. * @package SMF
  8. * @author Simple Machines http://www.simplemachines.org
  9. * @copyright 2014 Simple Machines and individual contributors
  10. * @license http://www.simplemachines.org/about/smf/license.php BSD
  11. *
  12. * @version 2.1 Alpha 1
  13. */
  14. if (!defined('SMF'))
  15. die('No direct access...');
  16. /**
  17. * Entry point for the moderation center.
  18. *
  19. * @param bool $dont_call = false
  20. */
  21. function ModerationMain($dont_call = false)
  22. {
  23. global $txt, $context, $scripturl, $sc, $modSettings, $user_info, $sourcedir, $options, $smcFunc;
  24. // Don't run this twice... and don't conflict with the admin bar.
  25. if (isset($context['admin_area']))
  26. return;
  27. $context['can_moderate_boards'] = $user_info['mod_cache']['bq'] != '0=1';
  28. $context['can_moderate_groups'] = $user_info['mod_cache']['gq'] != '0=1';
  29. $context['can_moderate_approvals'] = $modSettings['postmod_active'] && !empty($user_info['mod_cache']['ap']);
  30. $context['can_moderate_users'] = allowedTo('moderate_forum');
  31. // Everyone using this area must be allowed here!
  32. if (!$context['can_moderate_boards'] && !$context['can_moderate_groups'] && !$context['can_moderate_approvals'] && !$context['can_moderate_users'])
  33. isAllowedTo('access_mod_center');
  34. // We're gonna want a menu of some kind.
  35. require_once($sourcedir . '/Subs-Menu.php');
  36. // Load the language, and the template.
  37. loadLanguage('ModerationCenter');
  38. loadTemplate(false, 'admin');
  39. $context['admin_preferences'] = !empty($options['admin_preferences']) ? unserialize($options['admin_preferences']) : array();
  40. $context['robot_no_index'] = true;
  41. // This is the menu structure - refer to Subs-Menu.php for the details.
  42. $moderation_areas = array(
  43. 'main' => array(
  44. 'title' => $txt['mc_main'],
  45. 'areas' => array(
  46. 'index' => array(
  47. 'label' => $txt['moderation_center'],
  48. 'function' => 'ModerationHome',
  49. 'icon' => 'administration.png',
  50. ),
  51. 'settings' => array(
  52. 'label' => $txt['mc_settings'],
  53. 'function' => 'ModerationSettings',
  54. 'icon' => 'features.png',
  55. ),
  56. 'modlogoff' => array(
  57. 'label' => $txt['mc_logoff'],
  58. 'function' => 'ModEndSession',
  59. 'enabled' => empty($modSettings['securityDisable_moderate']),
  60. 'icon' => 'exit.png',
  61. ),
  62. 'notice' => array(
  63. 'file' => 'ModerationCenter.php',
  64. 'function' => 'ShowNotice',
  65. 'select' => 'index'
  66. ),
  67. ),
  68. ),
  69. 'logs' => array(
  70. 'title' => $txt['mc_logs'],
  71. 'areas' => array(
  72. 'modlog' => array(
  73. 'label' => $txt['modlog_view'],
  74. 'enabled' => !empty($modSettings['modlog_enabled']) && $context['can_moderate_boards'],
  75. 'file' => 'Modlog.php',
  76. 'function' => 'ViewModlog',
  77. 'icon' => 'logs.png',
  78. ),
  79. 'warnings' => array(
  80. 'label' => $txt['mc_warnings'],
  81. 'enabled' => $modSettings['warning_settings'][0] == 1 && $context['can_moderate_boards'],
  82. 'function' => 'ViewWarnings',
  83. 'icon' => 'warning.png',
  84. 'subsections' => array(
  85. 'log' => array($txt['mc_warning_log']),
  86. 'templates' => array($txt['mc_warning_templates'], 'issue_warning'),
  87. ),
  88. ),
  89. ),
  90. ),
  91. 'posts' => array(
  92. 'title' => $txt['mc_posts'],
  93. 'enabled' => $context['can_moderate_boards'] || $context['can_moderate_approvals'],
  94. 'areas' => array(
  95. 'postmod' => array(
  96. 'label' => $txt['mc_unapproved_posts'],
  97. 'enabled' => $context['can_moderate_approvals'],
  98. 'file' => 'PostModeration.php',
  99. 'function' => 'PostModerationMain',
  100. 'icon' => 'posts.png',
  101. 'custom_url' => $scripturl . '?action=moderate;area=postmod',
  102. 'subsections' => array(
  103. 'posts' => array($txt['mc_unapproved_replies']),
  104. 'topics' => array($txt['mc_unapproved_topics']),
  105. ),
  106. ),
  107. 'attachmod' => array(
  108. 'label' => $txt['mc_unapproved_attachments'],
  109. 'enabled' => $context['can_moderate_approvals'],
  110. 'file' => 'PostModeration.php',
  111. 'function' => 'PostModerationMain',
  112. 'icon' => 'post_moderation_attach.png',
  113. 'custom_url' => $scripturl . '?action=moderate;area=attachmod;sa=attachments',
  114. ),
  115. 'reports' => array(
  116. 'label' => $txt['mc_reported_posts'],
  117. 'enabled' => $context['can_moderate_boards'],
  118. 'file' => 'ReportedPosts.php',
  119. 'function' => 'ReportedPosts',
  120. 'icon' => 'reports.png',
  121. 'subsections' => array(
  122. 'show' => array($txt['mc_reportedp_active']),
  123. 'closed' => array($txt['mc_reportedp_closed']),
  124. ),
  125. ),
  126. ),
  127. ),
  128. 'groups' => array(
  129. 'title' => $txt['mc_groups'],
  130. 'enabled' => $context['can_moderate_groups'],
  131. 'areas' => array(
  132. 'groups' => array(
  133. 'label' => $txt['mc_group_requests'],
  134. 'file' => 'Groups.php',
  135. 'function' => 'Groups',
  136. 'icon' => 'members_request.png',
  137. 'custom_url' => $scripturl . '?action=moderate;area=groups;sa=requests',
  138. ),
  139. 'viewgroups' => array(
  140. 'label' => $txt['mc_view_groups'],
  141. 'file' => 'Groups.php',
  142. 'function' => 'Groups',
  143. 'icon' => 'membergroups.png',
  144. ),
  145. ),
  146. ),
  147. 'members' => array(
  148. 'title' => $txt['mc_members'],
  149. 'enabled' => $context['can_moderate_users'] || ($modSettings['warning_settings'][0] == 1 && $context['can_moderate_boards']),
  150. 'areas' => array(
  151. 'userwatch' => array(
  152. 'label' => $txt['mc_watched_users_title'],
  153. 'enabled' => $modSettings['warning_settings'][0] == 1 && $context['can_moderate_boards'],
  154. 'function' => 'ViewWatchedUsers',
  155. 'icon' => 'members_watched.png',
  156. 'subsections' => array(
  157. 'member' => array($txt['mc_watched_users_member']),
  158. 'post' => array($txt['mc_watched_users_post']),
  159. ),
  160. ),
  161. 'memberreports' => array(
  162. 'label' => $txt['mc_reported_members_title'],
  163. 'enabled' => $context['can_moderate_users'],
  164. 'function' => 'ReportedMembers',
  165. 'icon' => 'members_watched.png',
  166. 'subsections' => array(
  167. 'open' => array($txt['mc_reportedp_active']),
  168. 'closed' => array($txt['mc_reportedp_closed']),
  169. ),
  170. ),
  171. ),
  172. )
  173. );
  174. // Make sure the administrator has a valid session...
  175. validateSession('moderate');
  176. // I don't know where we're going - I don't know where we've been...
  177. $menuOptions = array(
  178. 'action' => 'moderate',
  179. 'disable_url_session_check' => true,
  180. );
  181. $mod_include_data = createMenu($moderation_areas, $menuOptions);
  182. unset($moderation_areas);
  183. // We got something - didn't we? DIDN'T WE!
  184. if ($mod_include_data == false)
  185. fatal_lang_error('no_access', false);
  186. // Retain the ID information in case required by a subaction.
  187. $context['moderation_menu_id'] = $context['max_menu_id'];
  188. $context['moderation_menu_name'] = 'menu_data_' . $context['moderation_menu_id'];
  189. // @todo: html in here is not good
  190. $context[$context['moderation_menu_name']]['tab_data'] = array(
  191. 'title' => $txt['moderation_center'],
  192. 'help' => '',
  193. 'description' => '
  194. <strong>' . $txt['hello_guest'] . ' ' . $context['user']['name'] . '!</strong>
  195. <br><br>
  196. ' . $txt['mc_description']);
  197. // What a pleasant shortcut - even tho we're not *really* on the admin screen who cares...
  198. $context['admin_area'] = $mod_include_data['current_area'];
  199. // Build the link tree.
  200. $context['linktree'][] = array(
  201. 'url' => $scripturl . '?action=moderate',
  202. 'name' => $txt['moderation_center'],
  203. );
  204. if (isset($mod_include_data['current_area']) && $mod_include_data['current_area'] != 'index')
  205. $context['linktree'][] = array(
  206. 'url' => $scripturl . '?action=moderate;area=' . $mod_include_data['current_area'],
  207. 'name' => $mod_include_data['label'],
  208. );
  209. if (!empty($mod_include_data['current_subsection']) && $mod_include_data['subsections'][$mod_include_data['current_subsection']][0] != $mod_include_data['label'])
  210. $context['linktree'][] = array(
  211. 'url' => $scripturl . '?action=moderate;area=' . $mod_include_data['current_area'] . ';sa=' . $mod_include_data['current_subsection'],
  212. 'name' => $mod_include_data['subsections'][$mod_include_data['current_subsection']][0],
  213. );
  214. // Now - finally - the bit before the encore - the main performance of course!
  215. if (!$dont_call)
  216. {
  217. if (isset($mod_include_data['file']))
  218. require_once($sourcedir . '/' . $mod_include_data['file']);
  219. $mod_include_data['function']();
  220. }
  221. }
  222. /**
  223. * This function basically is the home page of the moderation center.
  224. */
  225. function ModerationHome()
  226. {
  227. global $txt, $context, $scripturl, $user_settings, $options;
  228. loadTemplate('ModerationCenter');
  229. loadJavascriptFile('admin.js', array('default_theme' => true), 'admin.js');
  230. $context['page_title'] = $txt['moderation_center'];
  231. $context['sub_template'] = 'moderation_center';
  232. // Handle moderators notes.
  233. ModBlockNotes();
  234. // Load what blocks the user actually can see...
  235. $valid_blocks = array();
  236. if ($context['can_moderate_groups'])
  237. $valid_blocks['g'] = 'GroupRequests';
  238. if ($context['can_moderate_boards'])
  239. {
  240. $valid_blocks['r'] = 'ReportedPosts';
  241. $valid_blocks['w'] = 'WatchedUsers';
  242. }
  243. if ($context['can_moderate_users'])
  244. {
  245. // This falls under the category of moderating users as well...
  246. if (!$context['can_moderate_boards'])
  247. $valid_blocks['w'] = 'WatchedUsers';
  248. $valid_blocks['rm'] = 'ReportedMembers';
  249. }
  250. call_integration_hook('integrate_mod_centre_blocks', array(&$valid_blocks));
  251. $context['mod_blocks'] = array();
  252. foreach ($valid_blocks as $k => $block)
  253. {
  254. $block = 'ModBlock' . $block;
  255. if (function_exists($block))
  256. $context['mod_blocks'][] = $block();
  257. }
  258. $context['admin_prefs'] = !empty($options['admin_preferences']) ? unserialize($options['admin_preferences']) : array();
  259. }
  260. /**
  261. * Show a list of the most active watched users.
  262. */
  263. function ModBlockWatchedUsers()
  264. {
  265. global $context, $smcFunc, $scripturl, $modSettings;
  266. if (($watched_users = cache_get_data('recent_user_watches', 240)) === null)
  267. {
  268. $modSettings['warning_watch'] = empty($modSettings['warning_watch']) ? 1 : $modSettings['warning_watch'];
  269. $request = $smcFunc['db_query']('', '
  270. SELECT id_member, real_name, last_login
  271. FROM {db_prefix}members
  272. WHERE warning >= {int:warning_watch}
  273. ORDER BY last_login DESC
  274. LIMIT 10',
  275. array(
  276. 'warning_watch' => $modSettings['warning_watch'],
  277. )
  278. );
  279. $watched_users = array();
  280. while ($row = $smcFunc['db_fetch_assoc']($request))
  281. $watched_users[] = $row;
  282. $smcFunc['db_free_result']($request);
  283. cache_put_data('recent_user_watches', $watched_users, 240);
  284. }
  285. $context['watched_users'] = array();
  286. foreach ($watched_users as $user)
  287. {
  288. $context['watched_users'][] = array(
  289. 'id' => $user['id_member'],
  290. 'name' => $user['real_name'],
  291. 'link' => '<a href="' . $scripturl . '?action=profile;u=' . $user['id_member'] . '">' . $user['real_name'] . '</a>',
  292. 'href' => $scripturl . '?action=profile;u=' . $user['id_member'],
  293. 'last_login' => !empty($user['last_login']) ? timeformat($user['last_login']) : '',
  294. );
  295. }
  296. return 'watched_users';
  297. }
  298. /**
  299. * Show an area for the moderator to type into.
  300. */
  301. function ModBlockNotes()
  302. {
  303. global $context, $smcFunc, $scripturl, $txt, $user_info;
  304. // Set a nice and informative message.
  305. $context['report_post_action'] = !empty($_SESSION['rc_confirmation']) ? $_SESSION['rc_confirmation'] : array();
  306. unset($_SESSION['rc_confirmation']);
  307. // Are we saving a note?
  308. if (isset($_GET['modnote']) && isset($_POST['makenote']) && isset($_POST['new_note']))
  309. {
  310. checkSession();
  311. validateToken('mod-modnote-add');
  312. $_POST['new_note'] = $smcFunc['htmlspecialchars'](trim($_POST['new_note']));
  313. // Make sure they actually entered something.
  314. if (!empty($_POST['new_note']) && $_POST['new_note'] !== $txt['mc_click_add_note'])
  315. {
  316. // Insert it into the database then!
  317. $smcFunc['db_insert']('',
  318. '{db_prefix}log_comments',
  319. array(
  320. 'id_member' => 'int', 'member_name' => 'string', 'comment_type' => 'string', 'recipient_name' => 'string',
  321. 'body' => 'string', 'log_time' => 'int',
  322. ),
  323. array(
  324. $user_info['id'], $user_info['name'], 'modnote', '', $_POST['new_note'], time(),
  325. ),
  326. array('id_comment')
  327. );
  328. // Clear the cache.
  329. cache_put_data('moderator_notes', null, 240);
  330. cache_put_data('moderator_notes_total', null, 240);
  331. }
  332. // Everything went better than expected!
  333. $_SESSION['rc_confirmation'] = 'message_saved';
  334. // Redirect otherwise people can resubmit.
  335. redirectexit('action=moderate');
  336. }
  337. // Bye... bye...
  338. if (isset($_GET['notes']) && isset($_GET['delete']) && is_numeric($_GET['delete']))
  339. {
  340. checkSession('get');
  341. validateToken('mod-modnote-del', 'get');
  342. // Lets delete it.
  343. $smcFunc['db_query']('', '
  344. DELETE FROM {db_prefix}log_comments
  345. WHERE id_comment = {int:note}
  346. AND comment_type = {literal:modnote}',
  347. array(
  348. 'note' => $_GET['delete'],
  349. )
  350. );
  351. // Clear the cache.
  352. cache_put_data('moderator_notes', null, 240);
  353. cache_put_data('moderator_notes_total', null, 240);
  354. // Tell them the message was deleted.
  355. $_SESSION['rc_confirmation'] = 'message_deleted';
  356. redirectexit('action=moderate');
  357. }
  358. // How many notes in total?
  359. if (($moderator_notes_total = cache_get_data('moderator_notes_total', 240)) === null)
  360. {
  361. $request = $smcFunc['db_query']('', '
  362. SELECT COUNT(*)
  363. FROM {db_prefix}log_comments AS lc
  364. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
  365. WHERE lc.comment_type = {literal:modnote}',
  366. array(
  367. )
  368. );
  369. list ($moderator_notes_total) = $smcFunc['db_fetch_row']($request);
  370. $smcFunc['db_free_result']($request);
  371. cache_put_data('moderator_notes_total', $moderator_notes_total, 240);
  372. }
  373. // Grab the current notes. We can only use the cache for the first page of notes.
  374. $offset = isset($_GET['notes']) && isset($_GET['start']) ? $_GET['start'] : 0;
  375. if ($offset != 0 || ($moderator_notes = cache_get_data('moderator_notes', 240)) === null)
  376. {
  377. $request = $smcFunc['db_query']('', '
  378. SELECT IFNULL(mem.id_member, 0) AS id_member, IFNULL(mem.real_name, lc.member_name) AS member_name,
  379. lc.log_time, lc.body, lc.id_comment AS id_note
  380. FROM {db_prefix}log_comments AS lc
  381. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
  382. WHERE lc.comment_type = {literal:modnote}
  383. ORDER BY id_comment DESC
  384. LIMIT {int:offset}, 10',
  385. array(
  386. 'offset' => $offset,
  387. )
  388. );
  389. $moderator_notes = array();
  390. while ($row = $smcFunc['db_fetch_assoc']($request))
  391. $moderator_notes[] = $row;
  392. $smcFunc['db_free_result']($request);
  393. if ($offset == 0)
  394. cache_put_data('moderator_notes', $moderator_notes, 240);
  395. }
  396. // Lets construct a page index.
  397. $context['page_index'] = constructPageIndex($scripturl . '?action=moderate;area=index;notes', $_GET['start'], $moderator_notes_total, 10);
  398. $context['start'] = $_GET['start'];
  399. $context['notes'] = array();
  400. foreach ($moderator_notes as $note)
  401. {
  402. $context['notes'][] = array(
  403. 'author' => array(
  404. 'id' => $note['id_member'],
  405. 'link' => $note['id_member'] ? ('<a href="' . $scripturl . '?action=profile;u=' . $note['id_member'] . '">' . $note['member_name'] . '</a>') : $note['member_name'],
  406. ),
  407. 'time' => timeformat($note['log_time']),
  408. 'text' => parse_bbc($note['body']),
  409. 'delete_href' => $scripturl . '?action=moderate;area=index;notes;delete=' . $note['id_note'] . ';' . $context['session_var'] . '=' . $context['session_id'],
  410. );
  411. }
  412. // Add a confirm on deleting a modnote
  413. addInlineJavascript('
  414. $(\'.delete_modnote\').on(\'click\', function(){
  415. return confirm('. JavaScriptEscape($txt['mc_reportedp_delete_confirm']) .');
  416. });', true);
  417. // Couple tokens for add/delete modnotes
  418. createToken('mod-modnote-add');
  419. createToken('mod-modnote-del', 'get');
  420. return 'notes';
  421. }
  422. /**
  423. * Show a list of the most recent reported posts.
  424. */
  425. function ModBlockReportedPosts()
  426. {
  427. global $context, $user_info, $scripturl, $smcFunc;
  428. // Got the info already?
  429. $cachekey = md5(serialize($user_info['mod_cache']['bq']));
  430. $context['reported_posts'] = array();
  431. if ($user_info['mod_cache']['bq'] == '0=1')
  432. return 'reported_posts_block';
  433. if (($reported_posts = cache_get_data('reported_posts_' . $cachekey, 90)) === null)
  434. {
  435. // By George, that means we in a position to get the reports, jolly good.
  436. $request = $smcFunc['db_query']('', '
  437. SELECT lr.id_report, lr.id_msg, lr.id_topic, lr.id_board, lr.id_member, lr.subject,
  438. lr.num_reports, IFNULL(mem.real_name, lr.membername) AS author_name,
  439. IFNULL(mem.id_member, 0) AS id_author
  440. FROM {db_prefix}log_reported AS lr
  441. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member)
  442. WHERE ' . ($user_info['mod_cache']['bq'] == '1=1' || $user_info['mod_cache']['bq'] == '0=1' ? $user_info['mod_cache']['bq'] : 'lr.' . $user_info['mod_cache']['bq']) . '
  443. AND lr.id_board != {int:not_a_reported_post}
  444. AND lr.closed = {int:not_closed}
  445. AND lr.ignore_all = {int:not_ignored}
  446. ORDER BY lr.time_updated DESC
  447. LIMIT 10',
  448. array(
  449. 'not_a_reported_post' => 0,
  450. 'not_closed' => 0,
  451. 'not_ignored' => 0,
  452. )
  453. );
  454. $reported_posts = array();
  455. while ($row = $smcFunc['db_fetch_assoc']($request))
  456. $reported_posts[] = $row;
  457. $smcFunc['db_free_result']($request);
  458. // Cache it.
  459. cache_put_data('reported_posts_' . $cachekey, $reported_posts, 90);
  460. }
  461. $context['reported_posts'] = array();
  462. foreach ($reported_posts as $i => $row)
  463. {
  464. $context['reported_posts'][] = array(
  465. 'id' => $row['id_report'],
  466. 'alternate' => $i % 2,
  467. 'topic_href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
  468. 'report_href' => $scripturl . '?action=moderate;area=reports;report=' . $row['id_report'],
  469. 'author' => array(
  470. 'id' => $row['id_author'],
  471. 'name' => $row['author_name'],
  472. 'link' => $row['id_author'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_author'] . '">' . $row['author_name'] . '</a>' : $row['author_name'],
  473. 'href' => $scripturl . '?action=profile;u=' . $row['id_author'],
  474. ),
  475. 'comments' => array(),
  476. 'subject' => $row['subject'],
  477. 'num_reports' => $row['num_reports'],
  478. );
  479. }
  480. return 'reported_posts_block';
  481. }
  482. /**
  483. * Show a list of all the group requests they can see.
  484. */
  485. function ModBlockGroupRequests()
  486. {
  487. global $context, $user_info, $scripturl, $smcFunc;
  488. $context['group_requests'] = array();
  489. // Make sure they can even moderate someone!
  490. if ($user_info['mod_cache']['gq'] == '0=1')
  491. return 'group_requests_block';
  492. // What requests are outstanding?
  493. $request = $smcFunc['db_query']('', '
  494. SELECT lgr.id_request, lgr.id_member, lgr.id_group, lgr.time_applied, mem.member_name, mg.group_name, mem.real_name
  495. FROM {db_prefix}log_group_requests AS lgr
  496. INNER JOIN {db_prefix}members AS mem ON (mem.id_member = lgr.id_member)
  497. INNER JOIN {db_prefix}membergroups AS mg ON (mg.id_group = lgr.id_group)
  498. WHERE ' . ($user_info['mod_cache']['gq'] == '1=1' || $user_info['mod_cache']['gq'] == '0=1' ? $user_info['mod_cache']['gq'] : 'lgr.' . $user_info['mod_cache']['gq']) . '
  499. AND lgr.status = {int:status_open}
  500. ORDER BY lgr.id_request DESC
  501. LIMIT 10',
  502. array(
  503. 'status_open' => 0,
  504. )
  505. );
  506. for ($i = 0; $row = $smcFunc['db_fetch_assoc']($request); $i ++)
  507. {
  508. $context['group_requests'][] = array(
  509. 'id' => $row['id_request'],
  510. 'alternate' => $i % 2,
  511. 'request_href' => $scripturl . '?action=groups;sa=requests;gid=' . $row['id_group'],
  512. 'member' => array(
  513. 'id' => $row['id_member'],
  514. 'name' => $row['real_name'],
  515. 'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>',
  516. 'href' => $scripturl . '?action=profile;u=' . $row['id_member'],
  517. ),
  518. 'group' => array(
  519. 'id' => $row['id_group'],
  520. 'name' => $row['group_name'],
  521. ),
  522. 'time_submitted' => timeformat($row['time_applied']),
  523. );
  524. }
  525. $smcFunc['db_free_result']($request);
  526. return 'group_requests_block';
  527. }
  528. /**
  529. * Show a list of the most recent reported posts.
  530. */
  531. function ModBlockReportedMembers()
  532. {
  533. global $context, $user_info, $scripturl, $smcFunc;
  534. // Got the info already?
  535. $cachekey = md5(serialize((int) allowedTo('moderate_forum')));
  536. $context['reported_users'] = array();
  537. if (!allowedTo('moderate_forum'))
  538. return 'reported_users_block';
  539. if (($reported_posts = cache_get_data('reported_users_' . $cachekey, 90)) === null)
  540. {
  541. // By George, that means we in a position to get the reports, jolly good.
  542. $request = $smcFunc['db_query']('', '
  543. SELECT lr.id_report, lr.id_member,
  544. lr.num_reports, IFNULL(mem.real_name, lr.membername) AS user_name,
  545. IFNULL(mem.id_member, 0) AS id_user
  546. FROM {db_prefix}log_reported AS lr
  547. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member)
  548. WHERE lr.id_board = {int:not_a_reported_post}
  549. AND lr.closed = {int:not_closed}
  550. AND lr.ignore_all = {int:not_ignored}
  551. ORDER BY lr.time_updated DESC
  552. LIMIT 10',
  553. array(
  554. 'not_a_reported_post' => 0,
  555. 'not_closed' => 0,
  556. 'not_ignored' => 0,
  557. )
  558. );
  559. $reported_users = array();
  560. while ($row = $smcFunc['db_fetch_assoc']($request))
  561. $reported_users[] = $row;
  562. $smcFunc['db_free_result']($request);
  563. // Cache it.
  564. cache_put_data('reported_users_' . $cachekey, $reported_posts, 90);
  565. }
  566. $context['reported_users'] = array();
  567. foreach ($reported_users as $i => $row)
  568. {
  569. $context['reported_users'][] = array(
  570. 'id' => $row['id_report'],
  571. 'alternate' => $i % 2,
  572. 'report_href' => $scripturl . '?action=moderate;area=memberreports;report=' . $row['id_report'],
  573. 'user' => array(
  574. 'id' => $row['id_user'],
  575. 'name' => $row['user_name'],
  576. 'link' => $row['id_user'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_user'] . '">' . $row['user_name'] . '</a>' : $row['user_name'],
  577. 'href' => $scripturl . '?action=profile;u=' . $row['id_author'],
  578. ),
  579. 'comments' => array(),
  580. 'num_reports' => $row['num_reports'],
  581. );
  582. }
  583. return 'reported_users_block';
  584. }
  585. /**
  586. * Browse all the reported users...
  587. */
  588. function ReportedMembers()
  589. {
  590. global $txt, $context, $scripturl, $user_info, $smcFunc;
  591. loadTemplate('ModerationCenter');
  592. // Set an empty var for the server response.
  593. $context['report_member_action'] = '';
  594. // Put the open and closed options into tabs, because we can...
  595. $context[$context['moderation_menu_name']]['tab_data'] = array(
  596. 'title' => $txt['mc_reported_members'],
  597. 'help' => '',
  598. 'description' => $txt['mc_reported_members_desc'],
  599. );
  600. isAllowedTo('moderate_forum');
  601. // Are they wanting to view a particular report?
  602. if (!empty($_REQUEST['report']))
  603. return MemberReport();
  604. // Set up the comforting bits...
  605. $context['page_title'] = $txt['mc_reported_members'];
  606. $context['sub_template'] = 'reported_members';
  607. // Are we viewing open or closed reports?
  608. $context['view_closed'] = isset($_GET['sa']) && $_GET['sa'] == 'closed' ? 1 : 0;
  609. // Are we doing any work?
  610. if ((isset($_GET['ignore']) || isset($_GET['close'])) && isset($_GET['rid']))
  611. {
  612. checkSession('get');
  613. $_GET['rid'] = (int) $_GET['rid'];
  614. // Update the report...
  615. $smcFunc['db_query']('', '
  616. UPDATE {db_prefix}log_reported
  617. SET ' . (isset($_GET['ignore']) ? 'ignore_all = {int:ignore_all}' : 'closed = {int:closed}') . '
  618. WHERE id_report = {int:id_report}',
  619. array(
  620. 'ignore_all' => isset($_GET['ignore']) ? (int) $_GET['ignore'] : 0,
  621. 'closed' => isset($_GET['close']) ? (int) $_GET['close'] : 0,
  622. 'id_report' => $_GET['rid'],
  623. )
  624. );
  625. // Get the board, topic and message for this report
  626. $request = $smcFunc['db_query']('', '
  627. SELECT id_member, membername
  628. FROM {db_prefix}log_reported
  629. WHERE id_report = {int:id_report}',
  630. array(
  631. 'id_report' => $_GET['rid'],
  632. )
  633. );
  634. // Set up the data for the log...
  635. $extra = array('report' => $_GET['rid']);
  636. list($extra['member'], $extra['membername']) = $smcFunc['db_fetch_row']($request);
  637. $smcFunc['db_free_result']($request);
  638. // Stick this in string format for consistency
  639. $extra['member'] = (string)$extra['member'];
  640. // Tell the user about it.
  641. $context['report_member_action'] = isset($_GET['ignore']) ? (!empty($_GET['ignore']) ? 'ignore' : 'unignore') : (!empty($_GET['close']) ? 'close' : 'open');
  642. // Log this action
  643. logAction($context['report_member_action'] . '_user_report', $extra);
  644. // Time to update.
  645. updateSettings(array('last_mod_report_action' => time()));
  646. recountOpenMemberReports();
  647. }
  648. elseif (isset($_POST['close']) && isset($_POST['close_selected']))
  649. {
  650. checkSession();
  651. // All the ones to update...
  652. $toClose = array();
  653. foreach ($_POST['close'] as $rid)
  654. $toClose[] = (int) $rid;
  655. if (!empty($toClose))
  656. {
  657. // Get the data for each of these reports
  658. $request = $smcFunc['db_query']('', '
  659. SELECT id_report, id_member, membername
  660. FROM {db_prefix}log_reported
  661. WHERE id_report IN ({array_int:report_list})',
  662. array(
  663. 'report_list' => $toClose,
  664. )
  665. );
  666. while ($reports = $smcFunc['db_fetch_assoc']($request))
  667. {
  668. $report_data = array(
  669. 'report' => $reports['id_report'],
  670. 'membername' => $reports['membername'],
  671. 'member' => (string)$reports['id_member'],
  672. );
  673. // Log that this report was closed
  674. logAction('close_user_report', $report_data);
  675. }
  676. $smcFunc['db_free_result']($request);
  677. $smcFunc['db_query']('', '
  678. UPDATE {db_prefix}log_reported
  679. SET closed = {int:is_closed}
  680. WHERE id_report IN ({array_int:report_list})',
  681. array(
  682. 'report_list' => $toClose,
  683. 'is_closed' => 1,
  684. )
  685. );
  686. // Time to update.
  687. updateSettings(array('last_mod_report_action' => time()));
  688. recountOpenMemberReports();
  689. }
  690. // Go on and tell the result.
  691. $context['report_member_action'] = 'close_all';
  692. }
  693. // How many entries are we viewing?
  694. $request = $smcFunc['db_query']('', '
  695. SELECT COUNT(*)
  696. FROM {db_prefix}log_reported AS lr
  697. WHERE lr.closed = {int:view_closed}
  698. AND lr.id_board = {int:not_a_reported_post}',
  699. array(
  700. 'view_closed' => $context['view_closed'],
  701. 'not_a_reported_post' => 0,
  702. )
  703. );
  704. list ($context['total_reports']) = $smcFunc['db_fetch_row']($request);
  705. $smcFunc['db_free_result']($request);
  706. // So, that means we can page index, yes?
  707. $context['page_index'] = constructPageIndex($scripturl . '?action=moderate;area=memberreports' . ($context['view_closed'] ? ';sa=closed' : ''), $_GET['start'], $context['total_reports'], 10);
  708. $context['start'] = $_GET['start'];
  709. // By George, that means we in a position to get the reports, golly good.
  710. $request = $smcFunc['db_query']('', '
  711. SELECT lr.id_report, lr.id_member, lr.time_started, lr.time_updated, lr.num_reports, lr.closed, lr.ignore_all,
  712. IFNULL(mem.real_name, lr.membername) AS user_name, IFNULL(mem.id_member, 0) AS id_user
  713. FROM {db_prefix}log_reported AS lr
  714. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member)
  715. WHERE lr.closed = {int:view_closed}
  716. AND lr.id_board = {int:not_a_reported_post}
  717. ORDER BY lr.time_updated DESC
  718. LIMIT ' . $context['start'] . ', 10',
  719. array(
  720. 'view_closed' => $context['view_closed'],
  721. 'not_a_reported_post' => 0,
  722. )
  723. );
  724. $context['reports'] = array();
  725. $report_ids = array();
  726. for ($i = 0; $row = $smcFunc['db_fetch_assoc']($request); $i++)
  727. {
  728. $report_ids[] = $row['id_report'];
  729. $context['reports'][$row['id_report']] = array(
  730. 'id' => $row['id_report'],
  731. 'alternate' => $i % 2,
  732. 'report_href' => $scripturl . '?action=moderate;area=memberreports;report=' . $row['id_report'],
  733. 'user' => array(
  734. 'id' => $row['id_user'],
  735. 'name' => $row['user_name'],
  736. 'link' => $row['id_user'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_user'] . '">' . $row['user_name'] . '</a>' : $row['user_name'],
  737. 'href' => $scripturl . '?action=profile;u=' . $row['id_user'],
  738. ),
  739. 'comments' => array(),
  740. 'time_started' => timeformat($row['time_started']),
  741. 'last_updated' => timeformat($row['time_updated']),
  742. 'num_reports' => $row['num_reports'],
  743. 'closed' => $row['closed'],
  744. 'ignore' => $row['ignore_all']
  745. );
  746. }
  747. $smcFunc['db_free_result']($request);
  748. // Now get all the people who reported it.
  749. if (!empty($report_ids))
  750. {
  751. $request = $smcFunc['db_query']('', '
  752. SELECT lrc.id_comment, lrc.id_report, lrc.time_sent, lrc.comment,
  753. IFNULL(mem.id_member, 0) AS id_member, IFNULL(mem.real_name, lrc.membername) AS reporter
  754. FROM {db_prefix}log_reported_comments AS lrc
  755. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lrc.id_member)
  756. WHERE lrc.id_report IN ({array_int:report_list})',
  757. array(
  758. 'report_list' => $report_ids,
  759. )
  760. );
  761. while ($row = $smcFunc['db_fetch_assoc']($request))
  762. {
  763. $context['reports'][$row['id_report']]['comments'][] = array(
  764. 'id' => $row['id_comment'],
  765. 'message' => $row['comment'],
  766. 'time' => timeformat($row['time_sent']),
  767. 'member' => array(
  768. 'id' => $row['id_member'],
  769. 'name' => empty($row['reporter']) ? $txt['guest'] : $row['reporter'],
  770. 'link' => $row['id_member'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['reporter'] . '</a>' : (empty($row['reporter']) ? $txt['guest'] : $row['reporter']),
  771. 'href' => $row['id_member'] ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
  772. ),
  773. );
  774. }
  775. $smcFunc['db_free_result']($request);
  776. }
  777. $context['report_manage_bans'] = allowedTo('manage_bans');
  778. }
  779. /**
  780. * Act as an entrace for all group related activity.
  781. * @todo As for most things in this file, this needs to be moved somewhere appropriate?
  782. */
  783. function ModerateGroups()
  784. {
  785. global $txt, $context, $scripturl, $user_info;
  786. // You need to be allowed to moderate groups...
  787. if ($user_info['mod_cache']['gq'] == '0=1')
  788. isAllowedTo('manage_membergroups');
  789. // Load the group templates.
  790. loadTemplate('ModerationCenter');
  791. // Setup the subactions...
  792. $subactions = array(
  793. 'requests' => 'GroupRequests',
  794. 'view' => 'ViewGroups',
  795. );
  796. if (!isset($_GET['sa']) || !isset($subactions[$_GET['sa']]))
  797. $_GET['sa'] = 'view';
  798. $context['sub_action'] = $_GET['sa'];
  799. // Call the relevant function.
  800. $subactions[$context['sub_action']]();
  801. }
  802. /**
  803. * How many open reports do we have?
  804. */
  805. function recountOpenMemberReports()
  806. {
  807. global $user_info, $context, $smcFunc;
  808. $request = $smcFunc['db_query']('', '
  809. SELECT COUNT(*)
  810. FROM {db_prefix}log_reported
  811. WHERE closed = {int:not_closed}
  812. AND ignore_all = {int:not_ignored}
  813. AND id_board = {int:not_a_reported_post}',
  814. array(
  815. 'not_closed' => 0,
  816. 'not_ignored' => 0,
  817. 'not_a_reported_post' => 0,
  818. )
  819. );
  820. list ($open_reports) = $smcFunc['db_fetch_row']($request);
  821. $smcFunc['db_free_result']($request);
  822. $_SESSION['rmc'] = array(
  823. 'id' => $user_info['id'],
  824. 'time' => time(),
  825. 'reports' => $open_reports,
  826. );
  827. $context['open_member_reports'] = $open_reports;
  828. }
  829. function MemberReport()
  830. {
  831. global $user_info, $context, $sourcedir, $scripturl, $txt, $smcFunc;
  832. // Have to at least give us something
  833. if (empty($_REQUEST['report']))
  834. fatal_lang_error('mc_no_modreport_specified');
  835. // Integers only please
  836. $_REQUEST['report'] = (int) $_REQUEST['report'];
  837. // Get the report details, need this so we can limit access to a particular board
  838. $request = $smcFunc['db_query']('', '
  839. SELECT lr.id_report, lr.id_member,
  840. lr.time_started, lr.time_updated, lr.num_reports, lr.closed, lr.ignore_all,
  841. IFNULL(mem.real_name, lr.membername) AS user_name, IFNULL(mem.id_member, 0) AS id_user
  842. FROM {db_prefix}log_reported AS lr
  843. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member)
  844. WHERE lr.id_report = {int:id_report}
  845. AND lr.id_board = {int:not_a_reported_post}
  846. LIMIT 1',
  847. array(
  848. 'id_report' => $_REQUEST['report'],
  849. 'not_a_reported_post' => 0,
  850. )
  851. );
  852. // So did we find anything?
  853. if (!$smcFunc['db_num_rows']($request))
  854. fatal_lang_error('mc_no_modreport_found');
  855. // Woohoo we found a report and they can see it! Bad news is we have more work to do
  856. $row = $smcFunc['db_fetch_assoc']($request);
  857. $smcFunc['db_free_result']($request);
  858. // If they are adding a comment then... add a comment.
  859. if (isset($_POST['add_comment']) && !empty($_POST['mod_comment']))
  860. {
  861. checkSession();
  862. $newComment = trim($smcFunc['htmlspecialchars']($_POST['mod_comment']));
  863. // In it goes.
  864. if (!empty($newComment))
  865. {
  866. $smcFunc['db_insert']('',
  867. '{db_prefix}log_comments',
  868. array(
  869. 'id_member' => 'int', 'member_name' => 'string', 'comment_type' => 'string', 'recipient_name' => 'string',
  870. 'id_notice' => 'int', 'body' => 'string', 'log_time' => 'int',
  871. ),
  872. array(
  873. $user_info['id'], $user_info['name'], 'reportc', '',
  874. $_REQUEST['report'], $newComment, time(),
  875. ),
  876. array('id_comment')
  877. );
  878. $last_comment = $smcFunc['db_insert_id']('{db_prefix}log_comments', 'id_comment');
  879. // And get ready to notify people.
  880. $smcFunc['db_insert']('insert',
  881. '{db_prefix}background_tasks',
  882. array('task_file' => 'string', 'task_class' => 'string', 'task_data' => 'string', 'claimed_time' => 'int'),
  883. array('$sourcedir/tasks/MemberReportReply-Notify.php', 'MemberReportReply_Notify_Background', serialize(array(
  884. 'report_id' => $_REQUEST['report'],
  885. 'comment_id' => $last_comment,
  886. 'sender_name' => $user_info['name'],
  887. 'time' => time(),
  888. )), 0),
  889. array('id_task')
  890. );
  891. // Redirect to prevent double submittion.
  892. redirectexit($scripturl . '?action=moderate;area=memberreports;report=' . $_REQUEST['report']);
  893. }
  894. }
  895. $context['report'] = array(
  896. 'id' => $row['id_report'],
  897. 'report_href' => $scripturl . '?action=moderate;area=memberreports;report=' . $row['id_report'],
  898. 'user' => array(
  899. 'id' => $row['id_user'],
  900. 'name' => $row['user_name'],
  901. 'link' => $row['id_user'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_user'] . '">' . $row['user_name'] . '</a>' : $row['user_name'],
  902. 'href' => $scripturl . '?action=profile;u=' . $row['id_user'],
  903. ),
  904. 'comments' => array(),
  905. 'mod_comments' => array(),
  906. 'time_started' => timeformat($row['time_started']),
  907. 'last_updated' => timeformat($row['time_updated']),
  908. 'num_reports' => $row['num_reports'],
  909. 'closed' => $row['closed'],
  910. 'ignore' => $row['ignore_all']
  911. );
  912. // So what bad things do the reporters have to say about it?
  913. $request = $smcFunc['db_query']('', '
  914. SELECT lrc.id_comment, lrc.id_report, lrc.time_sent, lrc.comment, lrc.member_ip,
  915. IFNULL(mem.id_member, 0) AS id_member, IFNULL(mem.real_name, lrc.membername) AS reporter
  916. FROM {db_prefix}log_reported_comments AS lrc
  917. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lrc.id_member)
  918. WHERE lrc.id_report = {int:id_report}',
  919. array(
  920. 'id_report' => $context['report']['id'],
  921. )
  922. );
  923. while ($row = $smcFunc['db_fetch_assoc']($request))
  924. {
  925. $context['report']['comments'][] = array(
  926. 'id' => $row['id_comment'],
  927. 'message' => strtr($row['comment'], array("\n" => '<br>')),
  928. 'time' => timeformat($row['time_sent']),
  929. 'member' => array(
  930. 'id' => $row['id_member'],
  931. 'name' => empty($row['reporter']) ? $txt['guest'] : $row['reporter'],
  932. 'link' => $row['id_member'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['reporter'] . '</a>' : (empty($row['reporter']) ? $txt['guest'] : $row['reporter']),
  933. 'href' => $row['id_member'] ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
  934. 'ip' => !empty($row['member_ip']) && allowedTo('moderate_forum') ? '<a href="' . $scripturl . '?action=trackip;searchip=' . $row['member_ip'] . '">' . $row['member_ip'] . '</a>' : '',
  935. ),
  936. );
  937. }
  938. $smcFunc['db_free_result']($request);
  939. // Hang about old chap, any comments from moderators on this one?
  940. $request = $smcFunc['db_query']('', '
  941. SELECT lc.id_comment, lc.id_notice, lc.log_time, lc.body,
  942. IFNULL(mem.id_member, 0) AS id_member, IFNULL(mem.real_name, lc.member_name) AS moderator
  943. FROM {db_prefix}log_comments AS lc
  944. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
  945. WHERE lc.id_notice = {int:id_report}
  946. AND lc.comment_type = {literal:reportc}',
  947. array(
  948. 'id_report' => $context['report']['id'],
  949. )
  950. );
  951. while ($row = $smcFunc['db_fetch_assoc']($request))
  952. {
  953. $context['report']['mod_comments'][] = array(
  954. 'id' => $row['id_comment'],
  955. 'message' => parse_bbc($row['body']),
  956. 'time' => timeformat($row['log_time']),
  957. 'member' => array(
  958. 'id' => $row['id_member'],
  959. 'name' => $row['moderator'],
  960. 'link' => $row['id_member'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['moderator'] . '</a>' : $row['moderator'],
  961. 'href' => $scripturl . '?action=profile;u=' . $row['id_member'],
  962. ),
  963. );
  964. }
  965. $smcFunc['db_free_result']($request);
  966. // What have the other moderators done to this message?
  967. require_once($sourcedir . '/Modlog.php');
  968. require_once($sourcedir . '/Subs-List.php');
  969. loadLanguage('Modlog');
  970. // Find their ID in the serialized action string...
  971. $user_id_length = strlen((string)$context['report']['user']['id']);
  972. $member = 's:6:"member";s:' . $user_id_length . ':"' . $context['report']['user']['id'] . '";}';
  973. // This is all the information from the moderation log.
  974. // Note that we use "raw" here to prevent SMF from escaping things we don't want escaped
  975. $listOptions = array(
  976. 'id' => 'memreport_actions_list',
  977. 'title' => $txt['mc_modreport_modactions'],
  978. 'items_per_page' => 15,
  979. 'no_items_label' => $txt['modlog_no_entries_found'],
  980. 'base_href' => $scripturl . '?action=moderate;area=memberreports;report=' . $context['report']['id'],
  981. 'default_sort_col' => 'time',
  982. 'get_items' => array(
  983. 'function' => 'list_getModLogEntries',
  984. 'params' => array(
  985. 'lm.extra LIKE {raw:member}
  986. AND lm.action LIKE {raw:report}',
  987. array('member' => '\'%' . $member . '\'', 'report' => '\'%_user_report\''),
  988. 1,
  989. true,
  990. ),
  991. ),
  992. 'get_count' => array(
  993. 'function' => 'list_getModLogEntryCount',
  994. 'params' => array(
  995. 'lm.extra LIKE {string:member}
  996. AND lm.action LIKE {string:report}',
  997. array('member' => '%' . $member . '%', 'report' => '%_user_report'),
  998. 1,
  999. true,
  1000. ),
  1001. ),
  1002. // This assumes we are viewing by user.
  1003. 'columns' => array(
  1004. 'action' => array(
  1005. 'header' => array(
  1006. 'value' => $txt['modlog_action'],
  1007. ),
  1008. 'data' => array(
  1009. 'db' => 'action_text',
  1010. 'class' => 'smalltext',
  1011. ),
  1012. 'sort' => array(
  1013. 'default' => 'lm.action',
  1014. 'reverse' => 'lm.action DESC',
  1015. ),
  1016. ),
  1017. 'time' => array(
  1018. 'header' => array(
  1019. 'value' => $txt['modlog_date'],
  1020. ),
  1021. 'data' => array(
  1022. 'db' => 'time',
  1023. 'class' => 'smalltext',
  1024. ),
  1025. 'sort' => array(
  1026. 'default' => 'lm.log_time',
  1027. 'reverse' => 'lm.log_time DESC',
  1028. ),
  1029. ),
  1030. 'moderator' => array(
  1031. 'header' => array(
  1032. 'value' => $txt['modlog_member'],
  1033. ),
  1034. 'data' => array(
  1035. 'db' => 'moderator_link',
  1036. 'class' => 'smalltext',
  1037. ),
  1038. 'sort' => array(
  1039. 'default' => 'mem.real_name',
  1040. 'reverse' => 'mem.real_name DESC',
  1041. ),
  1042. ),
  1043. 'position' => array(
  1044. 'header' => array(
  1045. 'value' => $txt['modlog_position'],
  1046. ),
  1047. 'data' => array(
  1048. 'db' => 'position',
  1049. 'class' => 'smalltext',
  1050. ),
  1051. 'sort' => array(
  1052. 'default' => 'mg.group_name',
  1053. 'reverse' => 'mg.group_name DESC',
  1054. ),
  1055. ),
  1056. 'ip' => array(
  1057. 'header' => array(
  1058. 'value' => $txt['modlog_ip'],
  1059. ),
  1060. 'data' => array(
  1061. 'db' => 'ip',
  1062. 'class' => 'smalltext',
  1063. ),
  1064. 'sort' => array(
  1065. 'default' => 'lm.ip',
  1066. 'reverse' => 'lm.ip DESC',
  1067. ),
  1068. ),
  1069. ),
  1070. );
  1071. // Create the watched user list.
  1072. createList($listOptions);
  1073. // Make sure to get the correct tab selected.
  1074. if ($context['report']['closed'])
  1075. $context[$context['moderation_menu_name']]['current_subsection'] = 'closed';
  1076. // Finally we are done :P
  1077. loadTemplate('ModerationCenter');
  1078. $context['page_title'] = sprintf($txt['mc_viewmemberreport'], $context['report']['user']['name']);
  1079. $context['sub_template'] = 'viewmemberreport';
  1080. }
  1081. /**
  1082. * Show a notice sent to a user.
  1083. */
  1084. function ShowNotice()
  1085. {
  1086. global $smcFunc, $txt, $context;
  1087. $context['page_title'] = $txt['show_notice'];
  1088. $context['sub_template'] = 'show_notice';
  1089. $context['template_layers'] = array();
  1090. loadTemplate('ModerationCenter');
  1091. // @todo Assumes nothing needs permission more than accessing moderation center!
  1092. $id_notice = (int) $_GET['nid'];
  1093. $request = $smcFunc['db_query']('', '
  1094. SELECT body, subject
  1095. FROM {db_prefix}log_member_notices
  1096. WHERE id_notice = {int:id_notice}',
  1097. array(
  1098. 'id_notice' => $id_notice,
  1099. )
  1100. );
  1101. if ($smcFunc['db_num_rows']($request) == 0)
  1102. fatal_lang_error('no_access', false);
  1103. list ($context['notice_body'], $context['notice_subject']) = $smcFunc['db_fetch_row']($request);
  1104. $smcFunc['db_free_result']($request);
  1105. $context['notice_body'] = parse_bbc($context['notice_body'], false);
  1106. }
  1107. /**
  1108. * View watched users.
  1109. */
  1110. function ViewWatchedUsers()
  1111. {
  1112. global $smcFunc, $modSettings, $context, $txt, $scripturl, $sourcedir;
  1113. // Some important context!
  1114. $context['page_title'] = $txt['mc_watched_users_title'];
  1115. $context['view_posts'] = isset($_GET['sa']) && $_GET['sa'] == 'post';
  1116. $context['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0;
  1117. loadTemplate('ModerationCenter');
  1118. // Get some key settings!
  1119. $modSettings['warning_watch'] = empty($modSettings['warning_watch']) ? 1 : $modSettings['warning_watch'];
  1120. // Put some pretty tabs on cause we're gonna be doing hot stuff here...
  1121. $context[$context['moderation_menu_name']]['tab_data'] = array(
  1122. 'title' => $txt['mc_watched_users_title'],
  1123. 'help' => '',
  1124. 'description' => $txt['mc_watched_users_desc'],
  1125. );
  1126. // First off - are we deleting?
  1127. if (!empty($_REQUEST['delete']))
  1128. {
  1129. checkSession(!is_array($_REQUEST['delete']) ? 'get' : 'post');
  1130. $toDelete = array();
  1131. if (!is_array($_REQUEST['delete']))
  1132. $toDelete[] = (int) $_REQUEST['delete'];
  1133. else
  1134. foreach ($_REQUEST['delete'] as $did)
  1135. $toDelete[] = (int) $did;
  1136. if (!empty($toDelete))
  1137. {
  1138. require_once($sourcedir . '/RemoveTopic.php');
  1139. // If they don't have permission we'll let it error - either way no chance of a security slip here!
  1140. foreach ($toDelete as $did)
  1141. removeMessage($did);
  1142. }
  1143. }
  1144. // Start preparing the list by grabbing relevant permissions.
  1145. if (!$context['view_posts'])
  1146. {
  1147. $approve_query = '';
  1148. $delete_boards = array();
  1149. }
  1150. else
  1151. {
  1152. // Still obey permissions!
  1153. $approve_boards = boardsAllowedTo('approve_posts');
  1154. $delete_boards = boardsAllowedTo('delete_any');
  1155. if ($approve_boards == array(0))
  1156. $approve_query = '';
  1157. elseif (!empty($approve_boards))
  1158. $approve_query = ' AND m.id_board IN (' . implode(',', $approve_boards) . ')';
  1159. // Nada, zip, etc...
  1160. else
  1161. $approve_query = ' AND 1=0';
  1162. }
  1163. require_once($sourcedir . '/Subs-List.php');
  1164. // This is all the information required for a watched user listing.
  1165. $listOptions = array(
  1166. 'id' => 'watch_user_list',
  1167. 'title' => $txt['mc_watched_users_title'] . ' - ' . ($context['view_posts'] ? $txt['mc_watched_users_post'] : $txt['mc_watched_users_member']),
  1168. 'width' => '100%',
  1169. 'items_per_page' => $modSettings['defaultMaxMessages'],
  1170. 'no_items_label' => $context['view_posts'] ? $txt['mc_watched_users_no_posts'] : $txt['mc_watched_users_none'],
  1171. 'base_href' => $scripturl . '?action=moderate;area=userwatch;sa=' . ($context['view_posts'] ? 'post' : 'member'),
  1172. 'default_sort_col' => $context['view_posts'] ? '' : 'member',
  1173. 'get_items' => array(
  1174. 'function' => $context['view_posts'] ? 'list_getWatchedUserPosts' : 'list_getWatchedUsers',
  1175. 'params' => array(
  1176. $approve_query,
  1177. $delete_boards,
  1178. ),
  1179. ),
  1180. 'get_count' => array(
  1181. 'function' => $context['view_posts'] ? 'list_getWatchedUserPostsCount' : 'list_getWatchedUserCount',
  1182. 'params' => array(
  1183. $approve_query,
  1184. ),
  1185. ),
  1186. // This assumes we are viewing by user.
  1187. 'columns' => array(
  1188. 'member' => array(
  1189. 'header' => array(
  1190. 'value' => $txt['mc_watched_users_member'],
  1191. ),
  1192. 'data' => array(
  1193. 'sprintf' => array(
  1194. 'format' => '<a href="' . $scripturl . '?action=profile;u=%1$d">%2$s</a>',
  1195. 'params' => array(
  1196. 'id' => false,
  1197. 'name' => false,
  1198. ),
  1199. ),
  1200. ),
  1201. 'sort' => array(
  1202. 'default' => 'real_name',
  1203. 'reverse' => 'real_name DESC',
  1204. ),
  1205. ),
  1206. 'warning' => array(
  1207. 'header' => array(
  1208. 'value' => $txt['mc_watched_users_warning'],
  1209. ),
  1210. 'data' => array(
  1211. 'function' => create_function('$member', '
  1212. global $scripturl;
  1213. return allowedTo(\'issue_warning\') ? \'<a href="\' . $scripturl . \'?action=profile;area=issuewarning;u=\' . $member[\'id\'] . \'">\' . $member[\'warning\'] . \'%</a>\' : $member[\'warning\'] . \'%\';
  1214. '),
  1215. ),
  1216. 'sort' => array(
  1217. 'default' => 'warning',
  1218. 'reverse' => 'warning DESC',
  1219. ),
  1220. ),
  1221. 'posts' => array(
  1222. 'header' => array(
  1223. 'value' => $txt['posts'],
  1224. ),
  1225. 'data' => array(
  1226. 'sprintf' => array(
  1227. 'format' => '<a href="' . $scripturl . '?action=profile;u=%1$d;area=showposts;sa=messages">%2$s</a>',
  1228. 'params' => array(
  1229. 'id' => false,
  1230. 'posts' => false,
  1231. ),
  1232. ),
  1233. ),
  1234. 'sort' => array(
  1235. 'default' => 'posts',
  1236. 'reverse' => 'posts DESC',
  1237. ),
  1238. ),
  1239. 'last_login' => array(
  1240. 'header' => array(
  1241. 'value' => $txt['mc_watched_users_last_login'],
  1242. ),
  1243. 'data' => array(
  1244. 'db' => 'last_login',
  1245. ),
  1246. 'sort' => array(
  1247. 'default' => 'last_login',
  1248. 'reverse' => 'last_login DESC',
  1249. ),
  1250. ),
  1251. 'last_post' => array(
  1252. 'header' => array(
  1253. 'value' => $txt['mc_watched_users_last_post'],
  1254. ),
  1255. 'data' => array(
  1256. 'function' => create_function('$member', '
  1257. global $scripturl;
  1258. if ($member[\'last_post_id\'])
  1259. return \'<a href="\' . $scripturl . \'?msg=\' . $member[\'last_post_id\'] . \'">\' . $member[\'last_post\'] . \'</a>\';
  1260. else
  1261. return $member[\'last_post\'];
  1262. '),
  1263. ),
  1264. ),
  1265. ),
  1266. 'form' => array(
  1267. 'href' => $scripturl . '?action=moderate;area=userwatch;sa=post',
  1268. 'include_sort' => true,
  1269. 'include_start' => true,
  1270. 'hidden_fields' => array(
  1271. $context['session_var'] => $context['session_id'],
  1272. ),
  1273. ),
  1274. 'additional_rows' => array(
  1275. $context['view_posts'] ?
  1276. array(
  1277. 'position' => 'bottom_of_list',
  1278. 'value' => '
  1279. <input type="submit" name="delete_selected" value="' . $txt['quickmod_delete_selected'] . '" class="button_submit">',
  1280. 'align' => 'right',
  1281. ) : array(),
  1282. ),
  1283. );
  1284. // If this is being viewed by posts we actually change the columns to call a template each time.
  1285. if ($context['view_posts'])
  1286. {
  1287. $listOptions['columns'] = array(
  1288. 'posts' => array(
  1289. 'data' => array(
  1290. 'function' => create_function('$post', '
  1291. return template_user_watch_post_callback($post);
  1292. '),
  1293. ),
  1294. ),
  1295. );
  1296. }
  1297. // Create the watched user list.
  1298. createList($listOptions);
  1299. $context['sub_template'] = 'show_list';
  1300. $context['default_list'] = 'watch_user_list';
  1301. }
  1302. /**
  1303. * Callback for createList().
  1304. * @param $approve_query
  1305. */
  1306. function list_getWatchedUserCount($approve_query)
  1307. {
  1308. global $smcFunc, $modSettings;
  1309. $request = $smcFunc['db_query']('', '
  1310. SELECT COUNT(*)
  1311. FROM {db_prefix}members
  1312. WHERE warning >= {int:warning_watch}',
  1313. array(
  1314. 'warning_watch' => $modSettings['warning_watch'],
  1315. )
  1316. );
  1317. list ($totalMembers) = $smcFunc['db_fetch_row']($request);
  1318. $smcFunc['db_free_result']($request);
  1319. return $totalMembers;
  1320. }
  1321. /**
  1322. * Callback for createList().
  1323. *
  1324. * @param $start
  1325. * @param $items_per_page
  1326. * @param $sort
  1327. * @param $approve_query
  1328. * @param $dummy
  1329. */
  1330. function list_getWatchedUsers($start, $items_per_page, $sort, $approve_query, $dummy)
  1331. {
  1332. global $smcFunc, $txt, $scripturl, $modSettings, $user_info, $context;
  1333. $request = $smcFunc['db_query']('', '
  1334. SELECT id_member, real_name, last_login, posts, warning
  1335. FROM {db_prefix}members
  1336. WHERE warning >= {int:warning_watch}
  1337. ORDER BY {raw:sort}
  1338. LIMIT ' . $start . ', ' . $items_per_page,
  1339. array(
  1340. 'warning_watch' => $modSettings['warning_watch'],
  1341. 'sort' => $sort,
  1342. )
  1343. );
  1344. $watched_users = array();
  1345. $members = array();
  1346. while ($row = $smcFunc['db_fetch_assoc']($request))
  1347. {
  1348. $watched_users[$row['id_member']] = array(
  1349. 'id' => $row['id_member'],
  1350. 'name' => $row['real_name'],
  1351. 'last_login' => $row['last_login'] ? timeformat($row['last_login']) : $txt['never'],
  1352. 'last_post' => $txt['not_applicable'],
  1353. 'last_post_id' => 0,
  1354. 'warning' => $row['warning'],
  1355. 'posts' => $row['posts'],
  1356. );
  1357. $members[] = $row['id_member'];
  1358. }
  1359. $smcFunc['db_free_result']($request);
  1360. if (!empty($members))
  1361. {
  1362. // First get the latest messages from these users.
  1363. $request = $smcFunc['db_query']('', '
  1364. SELECT m.id_member, MAX(m.id_msg) AS last_post_id
  1365. FROM {db_prefix}messages AS m' . ($user_info['query_see_board'] == '1=1' ? '' : '
  1366. INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})') . '
  1367. WHERE m.id_member IN ({array_int:member_list})' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
  1368. AND m.approved = {int:is_approved}') . '
  1369. GROUP BY m.id_member',
  1370. array(
  1371. 'member_list' => $members,
  1372. 'is_approved' => 1,
  1373. )
  1374. );
  1375. $latest_posts = array();
  1376. while ($row = $smcFunc['db_fetch_assoc']($request))
  1377. $latest_posts[$row['id_member']] = $row['last_post_id'];
  1378. if (!empty($latest_posts))
  1379. {
  1380. // Now get the time those messages were posted.
  1381. $request = $smcFunc['db_query']('', '
  1382. SELECT id_member, poster_time
  1383. FROM {db_prefix}messages
  1384. WHERE id_msg IN ({array_int:message_list})',
  1385. array(
  1386. 'message_list' => $latest_posts,
  1387. )
  1388. );
  1389. while ($row = $smcFunc['db_fetch_assoc']($request))
  1390. {
  1391. $watched_users[$row['id_member']]['last_post'] = timeformat($row['poster_time']);
  1392. $watched_users[$row['id_member']]['last_post_id'] = $latest_posts[$row['id_member']];
  1393. }
  1394. $smcFunc['db_free_result']($request);
  1395. }
  1396. $request = $smcFunc['db_query']('', '
  1397. SELECT MAX(m.poster_time) AS last_post, MAX(m.id_msg) AS last_post_id, m.id_member
  1398. FROM {db_prefix}messages AS m' . ($user_info['query_see_board'] == '1=1' ? '' : '
  1399. INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})') . '
  1400. WHERE m.id_member IN ({array_int:member_list})' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
  1401. AND m.approved = {int:is_approved}') . '
  1402. GROUP BY m.id_member',
  1403. array(
  1404. 'member_list' => $members,
  1405. 'is_approved' => 1,
  1406. )
  1407. );
  1408. while ($row = $smcFunc['db_fetch_assoc']($request))
  1409. {
  1410. $watched_users[$row['id_member']]['last_post'] = timeformat($row['last_post']);
  1411. $watched_users[$row['id_member']]['last_post_id'] = $row['last_post_id'];
  1412. }
  1413. $smcFunc['db_free_result']($request);
  1414. }
  1415. return $watched_users;
  1416. }
  1417. /**
  1418. * Callback for createList().
  1419. *
  1420. * @param $approve_query
  1421. */
  1422. function list_getWatchedUserPostsCount($approve_query)
  1423. {
  1424. global $smcFunc, $modSettings;
  1425. $request = $smcFunc['db_query']('', '
  1426. SELECT COUNT(*)
  1427. FROM {db_prefix}messages AS m
  1428. INNER JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
  1429. INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
  1430. WHERE mem.warning >= {int:warning_watch}
  1431. AND {query_see_board}
  1432. ' . $approve_query,
  1433. array(
  1434. 'warning_watch' => $modSettings['warning_watch'],
  1435. )
  1436. );
  1437. list ($totalMemberPosts) = $smcFunc['db_fetch_row']($request);
  1438. $smcFunc['db_free_result']($request);
  1439. return $totalMemberPosts;
  1440. }
  1441. /**
  1442. * Callback for createList().
  1443. *
  1444. * @param $start
  1445. * @param $items_per_page
  1446. * @param $sort
  1447. * @param $approve_query
  1448. * @param $delete_boards
  1449. */
  1450. function list_getWatchedUserPosts($start, $items_per_page, $sort, $approve_query, $delete_boards)
  1451. {
  1452. global $smcFunc, $txt, $scripturl, $modSettings;
  1453. $request = $smcFunc['db_query']('', '
  1454. SELECT m.id_msg, m.id_topic, m.id_board, m.id_member, m.subject, m.body, m.poster_time,
  1455. m.approved, mem.real_name, m.smileys_enabled
  1456. FROM {db_prefix}messages AS m
  1457. INNER JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
  1458. INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
  1459. WHERE mem.warning >= {int:warning_watch}
  1460. AND {query_see_board}
  1461. ' . $approve_query . '
  1462. ORDER BY m.id_msg DESC
  1463. LIMIT ' . $start . ', ' . $items_per_page,
  1464. array(
  1465. 'warning_watch' => $modSettings['warning_watch'],
  1466. )
  1467. );
  1468. $member_posts = array();
  1469. while ($row = $smcFunc['db_fetch_assoc']($request))
  1470. {
  1471. $row['subject'] = censorText($row['subject']);
  1472. $row['body'] = censorText($row['body']);
  1473. $member_posts[$row['id_msg']] = array(
  1474. 'id' => $row['id_msg'],
  1475. 'id_topic' => $row['id_topic'],
  1476. 'author_link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>',
  1477. 'subject' => $row['subject'],
  1478. 'body' => parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']),
  1479. 'poster_time' => timeformat($row['poster_time']),
  1480. 'approved' => $row['approved'],
  1481. 'can_delete' => $delete_boards == array(0) || in_array($row['id_board'], $delete_boards),
  1482. );
  1483. }
  1484. $smcFunc['db_free_result']($request);
  1485. return $member_posts;
  1486. }
  1487. /**
  1488. * Entry point for viewing warning related stuff.
  1489. */
  1490. function ViewWarnings()
  1491. {
  1492. global $context, $txt;
  1493. $subActions = array(
  1494. 'log' => array('ViewWarningLog'),
  1495. 'templateedit' => array('ModifyWarningTemplate', 'issue_warning'),
  1496. 'templates' => array('ViewWarningTemplates', 'issue_warning'),
  1497. );
  1498. $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) && (empty($subActions[$_REQUEST['sa']][1]) || allowedTo($subActions[$_REQUEST['sa']]))? $_REQUEST['sa'] : 'log';
  1499. // Some of this stuff is overseas, so to speak.
  1500. loadTemplate('ModerationCenter');
  1501. loadLanguage('Profile');
  1502. // Setup the admin tabs.
  1503. $context[$context['moderation_menu_name']]['tab_data'] = array(
  1504. 'title' => $txt['mc_warnings'],
  1505. 'description' => $txt['mc_warnings_description'],
  1506. );
  1507. // Call the right function.
  1508. $subActions[$_REQUEST['sa']][0]();
  1509. }
  1510. /**
  1511. * Simply put, look at the warning log!
  1512. */
  1513. function ViewWarningLog()
  1514. {
  1515. global $smcFunc, $modSettings, $context, $txt, $scripturl, $sourcedir;
  1516. // Setup context as always.
  1517. $context['page_title'] = $txt['mc_warning_log_title'];
  1518. require_once($sourcedir . '/Subs-List.php');
  1519. // This is all the information required for a watched user listing.
  1520. $listOptions = array(
  1521. 'id' => 'warning_list',
  1522. 'title' => $txt['mc_warning_log_title'],
  1523. 'items_per_page' => $modSettings['defaultMaxMessages'],
  1524. 'no_items_label' => $txt['mc_warnings_none'],
  1525. 'base_href' => $scripturl . '?action=moderate;area=warnings;sa=log;' . $context['session_var'] . '=' . $context['session_id'],
  1526. 'default_sort_col' => 'time',
  1527. 'get_items' => array(
  1528. 'function' => 'list_getWarnings',
  1529. ),
  1530. 'get_count' => array(
  1531. 'function' => 'list_getWarningCount',
  1532. ),
  1533. // This assumes we are viewing by user.
  1534. 'columns' => array(
  1535. 'issuer' => array(
  1536. 'header' => array(
  1537. 'value' => $txt['profile_warning_previous_issued'],
  1538. ),
  1539. 'data' => array(
  1540. 'db' => 'issuer_link',
  1541. ),
  1542. 'sort' => array(
  1543. 'default' => 'member_name_col',
  1544. 'reverse' => 'member_name_col DESC',
  1545. ),
  1546. ),
  1547. 'recipient' => array(
  1548. 'header' => array(
  1549. 'value' => $txt['mc_warnings_recipient'],
  1550. ),
  1551. 'data' => array(
  1552. 'db' => 'recipient_link',
  1553. ),
  1554. 'sort' => array(
  1555. 'default' => 'recipient_name',
  1556. 'reverse' => 'recipient_name DESC',
  1557. ),
  1558. ),
  1559. 'time' => array(
  1560. 'header' => array(
  1561. 'value' => $txt['profile_warning_previous_time'],
  1562. ),
  1563. 'data' => array(
  1564. 'db' => 'time',
  1565. ),
  1566. 'sort' => array(
  1567. 'default' => 'lc.log_time DESC',
  1568. 'reverse' => 'lc.log_time',
  1569. ),
  1570. ),
  1571. 'reason' => array(
  1572. 'header' => array(
  1573. 'value' => $txt['profile_warning_previous_reason'],
  1574. ),
  1575. 'data' => array(
  1576. 'function' => create_function('$warning', '
  1577. global $scripturl, $txt;
  1578. $output = \'
  1579. <div class="floatleft">
  1580. \' . $warning[\'reason\'] . \'
  1581. </div>\';
  1582. if (!empty($warning[\'id_notice\']))
  1583. $output .= \'
  1584. &nbsp;<a href="\' . $scripturl . \'?action=moderate;area=notice;nid=\' . $warning[\'id_notice\'] . \'" onclick="window.open(this.href, \\\'\\\', \\\'scrollbars=yes,resizable=yes,width=400,height=250\\\');return false;" target="_blank" class="new_win" title="\' . $txt[\'profile_warning_previous_notice\'] . \'"><span class="generic_icons filter centericon"></span></a>\';
  1585. return $output;
  1586. '),
  1587. ),
  1588. ),
  1589. 'points' => array(
  1590. 'header' => array(
  1591. 'value' => $txt['profile_warning_previous_level'],
  1592. ),
  1593. 'data' => array(
  1594. 'db' => 'counter',
  1595. ),
  1596. ),
  1597. ),
  1598. );
  1599. // Create the watched user list.
  1600. createList($listOptions);
  1601. $context['sub_template'] = 'show_list';
  1602. $context['default_list'] = 'warning_list';
  1603. }
  1604. /**
  1605. * Callback for createList().
  1606. */
  1607. function list_getWarningCount()
  1608. {
  1609. global $smcFunc;
  1610. $request = $smcFunc['db_query']('', '
  1611. SELECT COUNT(*)
  1612. FROM {db_prefix}log_comments
  1613. WHERE comment_type = {string:warning}',
  1614. array(
  1615. 'warning' => 'warning',
  1616. )
  1617. );
  1618. list ($totalWarns) = $smcFunc['db_fetch_row']($request);
  1619. $smcFunc['db_free_result']($request);
  1620. return $totalWarns;
  1621. }
  1622. /**
  1623. * Callback for createList().
  1624. *
  1625. * @param $start
  1626. * @param $items_per_page
  1627. * @param $sort
  1628. */
  1629. function list_getWarnings($start, $items_per_page, $sort)
  1630. {
  1631. global $smcFunc, $txt, $scripturl;
  1632. $request = $smcFunc['db_query']('', '
  1633. SELECT IFNULL(mem.id_member, 0) AS id_member, IFNULL(mem.real_name, lc.member_name) AS member_name_col,
  1634. IFNULL(mem2.id_member, 0) AS id_recipient, IFNULL(mem2.real_name, lc.recipient_name) AS recipient_name,
  1635. lc.log_time, lc.body, lc.id_notice, lc.counter
  1636. FROM {db_prefix}log_comments AS lc
  1637. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
  1638. LEFT JOIN {db_prefix}members AS mem2 ON (mem2.id_member = lc.id_recipient)
  1639. WHERE lc.comment_type = {string:warning}
  1640. ORDER BY ' . $sort . '
  1641. LIMIT ' . $start . ', ' . $items_per_page,
  1642. array(
  1643. 'warning' => 'warning',
  1644. )
  1645. );
  1646. $warnings = array();
  1647. while ($row = $smcFunc['db_fetch_assoc']($request))
  1648. {
  1649. $warnings[] = array(
  1650. 'issuer_link' => $row['id_member'] ? ('<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['member_name_col'] . '</a>') : $row['member_name_col'],
  1651. 'recipient_link' => $row['id_recipient'] ? ('<a href="' . $scripturl . '?action=profile;u=' . $row['id_recipient'] . '">' . $row['recipient_name'] . '</a>') : $row['recipient_name'],
  1652. 'time' => timeformat($row['log_time']),
  1653. 'reason' => $row['body'],
  1654. 'counter' => $row['counter'] > 0 ? '+' . $row['counter'] : $row['counter'],
  1655. 'id_notice' => $row['id_notice'],
  1656. );
  1657. }
  1658. $smcFunc['db_free_result']($request);
  1659. return $warnings;
  1660. }
  1661. /**
  1662. * Load all the warning templates.
  1663. */
  1664. function ViewWarningTemplates()
  1665. {
  1666. global $smcFunc, $modSettings, $context, $txt, $scripturl, $sourcedir, $user_info;
  1667. // Submitting a new one?
  1668. if (isset($_POST['add']))
  1669. return ModifyWarningTemplate();
  1670. elseif (isset($_POST['delete']) && !empty($_POST['deltpl']))
  1671. {
  1672. checkSession();
  1673. validateToken('mod-wt');
  1674. // Log the actions.
  1675. $request = $smcFunc['db_query']('', '
  1676. SELECT recipient_name
  1677. FROM {db_prefix}log_comments
  1678. WHERE id_comment IN ({array_int:delete_ids})
  1679. AND comment_type = {string:warntpl}
  1680. AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})',
  1681. array(
  1682. 'delete_ids' => $_POST['deltpl'],
  1683. 'warntpl' => 'warntpl',
  1684. 'generic' => 0,
  1685. 'current_member' => $user_info['id'],
  1686. )
  1687. );
  1688. while ($row = $smcFunc['db_fetch_assoc']($request))
  1689. logAction('delete_warn_template', array('template' => $row['recipient_name']));
  1690. $smcFunc['db_free_result']($request);
  1691. // Do the deletes.
  1692. $smcFunc['db_query']('', '
  1693. DELETE FROM {db_prefix}log_comments
  1694. WHERE id_comment IN ({array_int:delete_ids})
  1695. AND comment_type = {string:warntpl}
  1696. AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})',
  1697. array(
  1698. 'delete_ids' => $_POST['deltpl'],
  1699. 'warntpl' => 'warntpl',
  1700. 'generic' => 0,
  1701. 'current_member' => $user_info['id'],
  1702. )
  1703. );
  1704. }
  1705. // Setup context as always.
  1706. $context['page_title'] = $txt['mc_warning_templates_title'];
  1707. require_once($sourcedir . '/Subs-List.php');
  1708. // This is all the information required for a watched user listing.
  1709. $listOptions = array(
  1710. 'id' => 'warning_template_list',
  1711. 'title' => $txt['mc_warning_templates_title'],
  1712. 'items_per_page' => $modSettings['defaultMaxMessages'],
  1713. 'no_items_label' => $txt['mc_warning_templates_none'],
  1714. 'base_href' => $scripturl . '?action=moderate;area=warnings;sa=templates;' . $context['session_var'] . '=' . $context['session_id'],
  1715. 'default_sort_col' => 'title',
  1716. 'get_items' => array(
  1717. 'function' => 'list_getWarningTemplates',
  1718. ),
  1719. 'get_count' => array(
  1720. 'function' => 'list_getWarningTemplateCount',
  1721. ),
  1722. // This assumes we are viewing by user.
  1723. 'columns' => array(
  1724. 'title' => array(
  1725. 'header' => array(
  1726. 'value' => $txt['mc_warning_templates_name'],
  1727. ),
  1728. 'data' => array(
  1729. 'sprintf' => array(
  1730. 'format' => '<a href="' . $scripturl . '?action=moderate;area=warnings;sa=templateedit;tid=%1$d">%2$s</a>',
  1731. 'params' => array(
  1732. 'id_comment' => false,
  1733. 'title' => false,
  1734. 'body' => false,
  1735. ),
  1736. ),
  1737. ),
  1738. 'sort' => array(
  1739. 'default' => 'template_title',
  1740. 'reverse' => 'template_title DESC',
  1741. ),
  1742. ),
  1743. 'creator' => array(
  1744. 'header' => array(
  1745. 'value' => $txt['mc_warning_templates_creator'],
  1746. ),
  1747. 'data' => array(
  1748. 'db' => 'creator',
  1749. ),
  1750. 'sort' => array(
  1751. 'default' => 'creator_name',
  1752. 'reverse' => 'creator_name DESC',
  1753. ),
  1754. ),
  1755. 'time' => array(
  1756. 'header' => array(
  1757. 'value' => $txt['mc_warning_templates_time'],
  1758. ),
  1759. 'data' => array(
  1760. 'db' => 'time',
  1761. ),
  1762. 'sort' => array(
  1763. 'default' => 'lc.log_time DESC',
  1764. 'reverse' => 'lc.log_time',
  1765. ),
  1766. ),
  1767. 'delete' => array(
  1768. 'header' => array(
  1769. 'value' => '<input type="checkbox" class="input_check" onclick="invertAll(this, this.form);">',
  1770. 'style' => 'width: 4%;',
  1771. 'class' => 'centercol',
  1772. ),
  1773. 'data' => array(
  1774. 'function' => create_function('$rowData', '
  1775. global $context, $txt, $scripturl;
  1776. return \'<input type="checkbox" name="deltpl[]" value="\' . $rowData[\'id_comment\'] . \'" class="input_check">\';
  1777. '),
  1778. 'class' => 'centercol',
  1779. ),
  1780. ),
  1781. ),
  1782. 'form' => array(
  1783. 'href' => $scripturl . '?action=moderate;area=warnings;sa=templates',
  1784. 'token' => 'mod-wt',
  1785. ),
  1786. 'additional_rows' => array(
  1787. array(
  1788. 'position' => 'below_table_data',
  1789. 'value' => '&nbsp;<input type="submit" name="delete" value="' . $txt['mc_warning_template_delete'] . '" onclick="return confirm(\'' . $txt['mc_warning_template_delete_confirm'] . '\');" class="button_submit">',
  1790. ),
  1791. array(
  1792. 'position' => 'bottom_of_list',
  1793. 'value' => '<input type="submit" name="add" value="' . $txt['mc_warning_template_add'] . '" class="button_submit">',
  1794. ),
  1795. ),
  1796. );
  1797. // Create the watched user list.
  1798. createToken('mod-wt');
  1799. createList($listOptions);
  1800. $context['sub_template'] = 'show_list';
  1801. $context['default_list'] = 'warning_template_list';
  1802. }
  1803. /**
  1804. * Callback for createList().
  1805. */
  1806. function list_getWarningTemplateCount()
  1807. {
  1808. global $smcFunc, $user_info;
  1809. $request = $smcFunc['db_query']('', '
  1810. SELECT COUNT(*)
  1811. FROM {db_prefix}log_comments
  1812. WHERE comment_type = {string:warntpl}
  1813. AND (id_recipient = {string:generic} OR id_recipient = {int:current_member})',
  1814. array(
  1815. 'warntpl' => 'warntpl',
  1816. 'generic' => 0,
  1817. 'current_member' => $user_info['id'],
  1818. )
  1819. );
  1820. list ($totalWarns) = $smcFunc['db_fetch_row']($request);
  1821. $smcFunc['db_free_result']($request);
  1822. return $totalWarns;
  1823. }
  1824. /**
  1825. * Callback for createList().
  1826. *
  1827. * @param $start
  1828. * @param $items_per_page
  1829. * @param $sort
  1830. */
  1831. function list_getWarningTemplates($start, $items_per_page, $sort)
  1832. {
  1833. global $smcFunc, $txt, $scripturl, $user_info;
  1834. $request = $smcFunc['db_query']('', '
  1835. SELECT lc.id_comment, IFNULL(mem.id_member, 0) AS id_member,
  1836. IFNULL(mem.real_name, lc.member_name) AS creator_name, recipient_name AS template_title,
  1837. lc.log_time, lc.body
  1838. FROM {db_prefix}log_comments AS lc
  1839. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
  1840. WHERE lc.comment_type = {string:warntpl}
  1841. AND (id_recipient = {string:generic} OR id_recipient = {int:current_member})
  1842. ORDER BY ' . $sort . '
  1843. LIMIT ' . $start . ', ' . $items_per_page,
  1844. array(
  1845. 'warntpl' => 'warntpl',
  1846. 'generic' => 0,
  1847. 'current_member' => $user_info['id'],
  1848. )
  1849. );
  1850. $templates = array();
  1851. while ($row = $smcFunc['db_fetch_assoc']($request))
  1852. {
  1853. $templates[] = array(
  1854. 'id_comment' => $row['id_comment'],
  1855. 'creator' => $row['id_member'] ? ('<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['creator_name'] . '</a>') : $row['creator_name'],
  1856. 'time' => timeformat($row['log_time']),
  1857. 'title' => $row['template_title'],
  1858. 'body' => $smcFunc['htmlspecialchars']($row['body']),
  1859. );
  1860. }
  1861. $smcFunc['db_free_result']($request);
  1862. return $templates;
  1863. }
  1864. /**
  1865. * Edit a warning template.
  1866. */
  1867. function ModifyWarningTemplate()
  1868. {
  1869. global $smcFunc, $context, $txt, $user_info, $sourcedir;
  1870. $context['id_template'] = isset($_REQUEST['tid']) ? (int) $_REQUEST['tid'] : 0;
  1871. $context['is_edit'] = $context['id_template'];
  1872. // Standard template things.
  1873. $context['page_title'] = $context['is_edit'] ? $txt['mc_warning_template_modify'] : $txt['mc_warning_template_add'];
  1874. $context['sub_template'] = 'warn_template';
  1875. $context[$context['moderation_menu_name']]['current_subsection'] = 'templates';
  1876. // Defaults.
  1877. $context['template_data'] = array(
  1878. 'title' => '',
  1879. 'body' => $txt['mc_warning_template_body_default'],
  1880. 'personal' => false,
  1881. 'can_edit_personal' => true,
  1882. );
  1883. // If it's an edit load it.
  1884. if ($context['is_edit'])
  1885. {
  1886. $request = $smcFunc['db_query']('', '
  1887. SELECT id_member, id_recipient, recipient_name AS template_title, body
  1888. FROM {db_prefix}log_comments
  1889. WHERE id_comment = {int:id}
  1890. AND comment_type = {string:warntpl}
  1891. AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})',
  1892. array(
  1893. 'id' => $context['id_template'],
  1894. 'warntpl' => 'warntpl',
  1895. 'generic' => 0,
  1896. 'current_member' => $user_info['id'],
  1897. )
  1898. );
  1899. while ($row = $smcFunc['db_fetch_assoc']($request))
  1900. {
  1901. $context['template_data'] = array(
  1902. 'title' => $row['template_title'],
  1903. 'body' => $smcFunc['htmlspecialchars']($row['body']),
  1904. 'personal' => $row['id_recipient'],
  1905. 'can_edit_personal' => $row['id_member'] == $user_info['id'],
  1906. );
  1907. }
  1908. $smcFunc['db_free_result']($request);
  1909. }
  1910. // Wait, we are saving?
  1911. if (isset($_POST['save']))
  1912. {
  1913. checkSession();
  1914. validateToken('mod-wt');
  1915. // To check the BBC is pretty good...
  1916. require_once($sourcedir . '/Subs-Post.php');
  1917. // Bit of cleaning!
  1918. $_POST['template_body'] = trim($_POST['template_body']);
  1919. $_POST['template_title'] = trim($_POST['template_title']);
  1920. // Need something in both boxes.
  1921. if (!empty($_POST['template_body']) && !empty($_POST['template_title']))
  1922. {
  1923. // Safety first.
  1924. $_POST['template_title'] = $smcFunc['htmlspecialchars']($_POST['template_title']);
  1925. // Clean up BBC.
  1926. preparsecode($_POST['template_body']);
  1927. // But put line breaks back!
  1928. $_POST['template_body'] = strtr($_POST['template_body'], array('<br>' => "\n"));
  1929. // Is this personal?
  1930. $recipient_id = !empty($_POST['make_personal']) ? $user_info['id'] : 0;
  1931. // If we are this far it's save time.
  1932. if ($context['is_edit'])
  1933. {
  1934. // Simple update...
  1935. $smcFunc['db_query']('', '
  1936. UPDATE {db_prefix}log_comments
  1937. SET id_recipient = {int:personal}, recipient_name = {string:title}, body = {string:body}
  1938. WHERE id_comment = {int:id}
  1939. AND comment_type = {string:warntpl}
  1940. AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})'.
  1941. ($recipient_id ? ' AND id_member = {int:current_member}' : ''),
  1942. array(
  1943. 'personal' => $recipient_id,
  1944. 'title' => $_POST['template_title'],
  1945. 'body' => $_POST['template_body'],
  1946. 'id' => $context['id_template'],
  1947. 'warntpl' => 'warntpl',
  1948. 'generic' => 0,
  1949. 'current_member' => $user_info['id'],
  1950. )
  1951. );
  1952. // If it wasn't visible and now is they've effectively added it.
  1953. if ($context['template_data']['personal'] && !$recipient_id)
  1954. logAction('add_warn_template', array('template' => $_POST['template_title']));
  1955. // Conversely if they made it personal it's a delete.
  1956. elseif (!$context['template_data']['personal'] && $recipient_id)
  1957. logAction('delete_warn_template', array('template' => $_POST['template_title']));
  1958. // Otherwise just an edit.
  1959. else
  1960. logAction('modify_warn_template', array('template' => $_POST['template_title']));
  1961. }
  1962. else
  1963. {
  1964. $smcFunc['db_insert']('',
  1965. '{db_prefix}log_comments',
  1966. array(
  1967. 'id_member' => 'int', 'member_name' => 'string', 'comment_type' => 'string', 'id_recipient' => 'int',
  1968. 'recipient_name' => 'string-255', 'body' => 'string-65535', 'log_time' => 'int',
  1969. ),
  1970. array(
  1971. $user_info['id'], $user_info['name'], 'warntpl', $recipient_id,
  1972. $_POST['template_title'], $_POST['template_body'], time(),
  1973. ),
  1974. array('id_comment')
  1975. );
  1976. logAction('add_warn_template', array('template' => $_POST['template_title']));
  1977. }
  1978. // Get out of town...
  1979. redirectexit('action=moderate;area=warnings;sa=templates');
  1980. }
  1981. else
  1982. {
  1983. $context['warning_errors'] = array();
  1984. $context['template_data']['title'] = !empty($_POST['template_title']) ? $_POST['template_title'] : '';
  1985. $context['template_data']['body'] = !empty($_POST['template_body']) ? $_POST['template_body'] : $txt['mc_warning_template_body_default'];
  1986. $context['template_data']['personal'] = !empty($_POST['make_personal']);
  1987. if (empty($_POST['template_title']))
  1988. $context['warning_errors'][] = $txt['mc_warning_template_error_no_title'];
  1989. if (empty($_POST['template_body']))
  1990. $context['warning_errors'][] = $txt['mc_warning_template_error_no_body'];
  1991. }
  1992. }
  1993. createToken('mod-wt');
  1994. }
  1995. /**
  1996. * Change moderation preferences.
  1997. */
  1998. function ModerationSettings()
  1999. {
  2000. global $context, $smcFunc, $txt, $sourcedir, $scripturl, $user_settings, $user_info;
  2001. // Some useful context stuff.
  2002. loadTemplate('ModerationCenter');
  2003. $context['page_title'] = $txt['mc_settings'];
  2004. $context['sub_template'] = 'moderation_settings';
  2005. $context[$context['moderation_menu_name']]['tab_data'] = array(
  2006. 'title' => $txt['mc_prefs_title'],
  2007. 'help' => '',
  2008. 'description' => $txt['mc_prefs_desc']
  2009. );
  2010. $mod_blocks = '';
  2011. $pref_binary = 5;
  2012. $show_reports = 0;
  2013. // Are we saving?
  2014. if (isset($_POST['save']))
  2015. {
  2016. checkSession();
  2017. validateToken('mod-set');
  2018. /* Current format of mod_prefs is:
  2019. x|ABCD|yyy
  2020. WHERE:
  2021. x = Show report count on forum header.
  2022. ABCD = Block indexes to show on moderation main page.
  2023. yyy = Integer with the following bit status:
  2024. - yyy & 4 = Notify about posts awaiting approval.
  2025. */
  2026. // Now check other options!
  2027. $pref_binary = 0;
  2028. if ($context['can_moderate_approvals'] && !empty($_POST['mod_notify_approval']))
  2029. $pref_binary |= 4;
  2030. // Put it all together.
  2031. $mod_prefs = '0||' . $pref_binary;
  2032. updateMemberData($user_info['id'], array('mod_prefs' => $mod_prefs));
  2033. }
  2034. // What blocks does the user currently have selected?
  2035. $context['mod_settings'] = array(
  2036. 'notify_approval' => $pref_binary & 4,
  2037. );
  2038. createToken('mod-set');
  2039. }
  2040. /**
  2041. * This ends a moderator session, requiring authentication to access the MCP again.
  2042. */
  2043. function ModEndSession()
  2044. {
  2045. // This is so easy!
  2046. unset($_SESSION['moderate_time']);
  2047. // Clean any moderator tokens as well.
  2048. foreach ($_SESSION['token'] as $key => $token)
  2049. if (strpos($key, '-mod') !== false)
  2050. unset($_SESSION['token'][$key]);
  2051. redirectexit();
  2052. }
  2053. ?>