Subs-BoardIndex.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. <?php
  2. /**
  3. * This file currently only contains one function to collect the data needed to
  4. * show a list of boards for the board index and the message index.
  5. *
  6. * Simple Machines Forum (SMF)
  7. *
  8. * @package SMF
  9. * @author Simple Machines http://www.simplemachines.org
  10. * @copyright 2014 Simple Machines and individual contributors
  11. * @license http://www.simplemachines.org/about/smf/license.php BSD
  12. *
  13. * @version 2.1 Alpha 1
  14. */
  15. if (!defined('SMF'))
  16. die('No direct access...');
  17. /**
  18. * Fetches a list of boards and (optional) categories including
  19. * statistical information, child boards and moderators.
  20. * - Used by both the board index (main data) and the message index (child
  21. * boards).
  22. * - Depending on the include_categories setting returns an associative
  23. * array with categories->boards->child_boards or an associative array
  24. * with boards->child_boards.
  25. * @param array $boardIndexOptions
  26. * @return array
  27. */
  28. function getBoardIndex($boardIndexOptions)
  29. {
  30. global $smcFunc, $scripturl, $user_info, $modSettings, $txt;
  31. global $settings, $context;
  32. // For performance, track the latest post while going through the boards.
  33. if (!empty($boardIndexOptions['set_latest_post']))
  34. $latest_post = array(
  35. 'timestamp' => 0,
  36. 'ref' => 0,
  37. );
  38. // Find all boards and categories, as well as related information. This will be sorted by the natural order of boards and categories, which we control.
  39. $result_boards = $smcFunc['db_query']('boardindex_fetch_boards', '
  40. SELECT' . ($boardIndexOptions['include_categories'] ? '
  41. c.id_cat, c.name AS cat_name, c.description AS cat_desc,' : '') . '
  42. b.id_board, b.name AS board_name, b.description,
  43. CASE WHEN b.redirect != {string:blank_string} THEN 1 ELSE 0 END AS is_redirect,
  44. b.num_posts, b.num_topics, b.unapproved_posts, b.unapproved_topics, b.id_parent,
  45. IFNULL(m.poster_time, 0) AS poster_time, IFNULL(mem.member_name, m.poster_name) AS poster_name,
  46. m.subject, m.id_topic, IFNULL(mem.real_name, m.poster_name) AS real_name,
  47. ' . ($user_info['is_guest'] ? ' 1 AS is_read, 0 AS new_from,' : '
  48. (IFNULL(lb.id_msg, 0) >= b.id_msg_updated) AS is_read, IFNULL(lb.id_msg, -1) + 1 AS new_from,' . ($boardIndexOptions['include_categories'] ? '
  49. c.can_collapse, IFNULL(cc.id_member, 0) AS is_collapsed,' : '')) . '
  50. IFNULL(mem.id_member, 0) AS id_member, mem.avatar, m.id_msg,
  51. IFNULL(mods_grp.id_group, 0) AS id_moderator_group, mods_grp.group_name AS mod_group_name,
  52. IFNULL(mods_mem.id_member, 0) AS id_moderator, mods_mem.real_name AS mod_real_name' . (!empty($settings['avatars_on_indexes']) ? ',
  53. IFNULL(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type' : '') . '
  54. FROM {db_prefix}boards AS b' . ($boardIndexOptions['include_categories'] ? '
  55. LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)' : '') . '
  56. LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = b.id_last_msg)
  57. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)' . ($user_info['is_guest'] ? '' : '
  58. LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = b.id_board AND lb.id_member = {int:current_member})' . ($boardIndexOptions['include_categories'] ? '
  59. LEFT JOIN {db_prefix}collapsed_categories AS cc ON (cc.id_cat = c.id_cat AND cc.id_member = {int:current_member})' : '')) . '
  60. LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board)
  61. LEFT JOIN {db_prefix}moderator_groups AS mods_g ON (mods_g.id_board = b.id_board)
  62. LEFT JOIN {db_prefix}membergroups AS mods_grp ON (mods_grp.id_group = mods_g.id_group)
  63. LEFT JOIN {db_prefix}members AS mods_mem ON (mods_mem.id_member = mods.id_member)' . (!empty($settings['avatars_on_indexes']) ? '
  64. LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = m.id_member)' : '') . '
  65. WHERE {query_see_board}' . (empty($boardIndexOptions['countChildPosts']) ? (empty($boardIndexOptions['base_level']) ? '' : '
  66. AND b.child_level >= {int:child_level}') : '
  67. AND b.child_level BETWEEN ' . $boardIndexOptions['base_level'] . ' AND ' . ($boardIndexOptions['base_level'] + 1)),
  68. array(
  69. 'current_member' => $user_info['id'],
  70. 'child_level' => $boardIndexOptions['base_level'],
  71. 'blank_string' => '',
  72. )
  73. );
  74. // Start with an empty array.
  75. if ($boardIndexOptions['include_categories'])
  76. $categories = array();
  77. else
  78. $this_category = array();
  79. // Run through the categories and boards (or only boards)....
  80. while ($row_board = $smcFunc['db_fetch_assoc']($result_boards))
  81. {
  82. // Perhaps we are ignoring this board?
  83. $ignoreThisBoard = in_array($row_board['id_board'], $user_info['ignoreboards']);
  84. $row_board['is_read'] = !empty($row_board['is_read']) || $ignoreThisBoard ? '1' : '0';
  85. if ($boardIndexOptions['include_categories'])
  86. {
  87. // Haven't set this category yet.
  88. if (empty($categories[$row_board['id_cat']]))
  89. {
  90. $categories[$row_board['id_cat']] = array(
  91. 'id' => $row_board['id_cat'],
  92. 'name' => $row_board['cat_name'],
  93. 'description' => $row_board['cat_desc'],
  94. 'is_collapsed' => isset($row_board['can_collapse']) && $row_board['can_collapse'] == 1 && $row_board['is_collapsed'] > 0,
  95. 'can_collapse' => isset($row_board['can_collapse']) && $row_board['can_collapse'] == 1,
  96. 'collapse_href' => isset($row_board['can_collapse']) ? $scripturl . '?action=collapse;c=' . $row_board['id_cat'] . ';sa=' . ($row_board['is_collapsed'] > 0 ? 'expand;' : 'collapse;') . $context['session_var'] . '=' . $context['session_id'] . '#c' . $row_board['id_cat'] : '',
  97. 'collapse_image' => isset($row_board['can_collapse']) ? '<span class="' . ($row_board['is_collapsed'] > 0 ? 'toggle_down' : 'toggle_up') . '"></span>' : '',
  98. 'href' => $scripturl . '#c' . $row_board['id_cat'],
  99. 'boards' => array(),
  100. 'new' => false
  101. );
  102. $categories[$row_board['id_cat']]['link'] = '<a id="c' . $row_board['id_cat'] . '"></a>' . (!$context['user']['is_guest'] ? '<a href="' . $scripturl . '?action=unread;c='. $row_board['id_cat'] . '" title="' . sprintf($txt['new_posts_in_category'], strip_tags($row_board['cat_name'])) . '">' . $row_board['cat_name'] . '</a>' : $row_board['cat_name']);
  103. }
  104. // If this board has new posts in it (and isn't the recycle bin!) then the category is new.
  105. if (empty($modSettings['recycle_enable']) || $modSettings['recycle_board'] != $row_board['id_board'])
  106. $categories[$row_board['id_cat']]['new'] |= empty($row_board['is_read']) && $row_board['poster_name'] != '';
  107. // Avoid showing category unread link where it only has redirection boards.
  108. $categories[$row_board['id_cat']]['show_unread'] = !empty($categories[$row_board['id_cat']]['show_unread']) ? 1 : !$row_board['is_redirect'];
  109. // Collapsed category - don't do any of this.
  110. if ($categories[$row_board['id_cat']]['is_collapsed'])
  111. continue;
  112. // Let's save some typing. Climbing the array might be slower, anyhow.
  113. $this_category = &$categories[$row_board['id_cat']]['boards'];
  114. }
  115. // This is a parent board.
  116. if ($row_board['id_parent'] == $boardIndexOptions['parent_id'])
  117. {
  118. // Is this a new board, or just another moderator?
  119. if (!isset($this_category[$row_board['id_board']]))
  120. {
  121. // Not a child.
  122. $isChild = false;
  123. $this_category[$row_board['id_board']] = array(
  124. 'new' => empty($row_board['is_read']),
  125. 'id' => $row_board['id_board'],
  126. 'name' => $row_board['board_name'],
  127. 'description' => $row_board['description'],
  128. 'moderators' => array(),
  129. 'moderator_groups' => array(),
  130. 'link_moderators' => array(),
  131. 'link_moderator_groups' => array(),
  132. 'children' => array(),
  133. 'link_children' => array(),
  134. 'children_new' => false,
  135. 'topics' => $row_board['num_topics'],
  136. 'posts' => $row_board['num_posts'],
  137. 'is_redirect' => $row_board['is_redirect'],
  138. 'unapproved_topics' => $row_board['unapproved_topics'],
  139. 'unapproved_posts' => $row_board['unapproved_posts'] - $row_board['unapproved_topics'],
  140. 'can_approve_posts' => !empty($user_info['mod_cache']['ap']) && ($user_info['mod_cache']['ap'] == array(0) || in_array($row_board['id_board'], $user_info['mod_cache']['ap'])),
  141. 'href' => $scripturl . '?board=' . $row_board['id_board'] . '.0',
  142. 'link' => '<a href="' . $scripturl . '?board=' . $row_board['id_board'] . '.0">' . $row_board['board_name'] . '</a>',
  143. 'board_class' => 'off',
  144. );
  145. // We can do some of the figuring-out-what-icon now.
  146. // For certain types of thing we also set up what the tooltip is.
  147. if ($this_category[$row_board['id_board']]['is_redirect'])
  148. {
  149. $this_category[$row_board['id_board']]['board_class'] = 'redirect';
  150. $this_category[$row_board['id_board']]['board_tooltip'] = $txt['redirect_board'];
  151. }
  152. elseif ($this_category[$row_board['id_board']]['new'] || $context['user']['is_guest'])
  153. {
  154. // If we're showing to guests, we want to give them the idea that something interesting is going on!
  155. $this_category[$row_board['id_board']]['board_class'] = 'on';
  156. $this_category[$row_board['id_board']]['board_tooltip'] = $txt['new_posts'];
  157. }
  158. else
  159. {
  160. $this_category[$row_board['id_board']]['board_tooltip'] = $txt['old_posts'];
  161. }
  162. }
  163. if (!empty($row_board['id_moderator']))
  164. {
  165. $this_category[$row_board['id_board']]['moderators'][$row_board['id_moderator']] = array(
  166. 'id' => $row_board['id_moderator'],
  167. 'name' => $row_board['mod_real_name'],
  168. 'href' => $scripturl . '?action=profile;u=' . $row_board['id_moderator'],
  169. 'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row_board['id_moderator'] . '" title="' . $txt['board_moderator'] . '">' . $row_board['mod_real_name'] . '</a>'
  170. );
  171. $this_category[$row_board['id_board']]['link_moderators'][] = '<a href="' . $scripturl . '?action=profile;u=' . $row_board['id_moderator'] . '" title="' . $txt['board_moderator'] . '">' . $row_board['mod_real_name'] . '</a>';
  172. }
  173. if (!empty($row_board['id_moderator_group']))
  174. {
  175. $this_category[$row_board['id_board']]['moderator_groups'][$row_board['id_moderator_group']] = array(
  176. 'id' => $row_board['id_moderator_group'],
  177. 'name' => $row_board['mod_group_name'],
  178. 'href' => $scripturl . '?action=groups;sa=members;group=' . $row_board['id_moderator_group'],
  179. 'link' => '<a href="' . $scripturl . '?action=groups;sa=members;group=' . $row_board['id_moderator_group'] . '" title="' . $txt['board_moderator'] . '">' . $row_board['mod_group_name'] . '</a>'
  180. );
  181. $this_category[$row_board['id_board']]['link_moderator_groups'][] = '<a href="' . $scripturl . '?action=groups;sa=members;group=' . $row_board['id_moderator_group'] . '" title="' . $txt['board_moderator'] . '">' . $row_board['mod_group_name'] . '</a>';
  182. }
  183. // Merge the two lists of moderators
  184. if (!empty($this_category[$row_board['id_board']]['link_moderator_groups']))
  185. {
  186. $this_category[$row_board['id_board']]['link_moderators'] = array_merge($this_category[$row_board['id_board']]['link_moderators'], $this_category[$row_board['id_board']]['link_moderator_groups']);
  187. }
  188. }
  189. // Found a child board.... make sure we've found its parent and the child hasn't been set already.
  190. elseif (isset($this_category[$row_board['id_parent']]['children']) && !isset($this_category[$row_board['id_parent']]['children'][$row_board['id_board']]))
  191. {
  192. // A valid child!
  193. $isChild = true;
  194. $this_category[$row_board['id_parent']]['children'][$row_board['id_board']] = array(
  195. 'id' => $row_board['id_board'],
  196. 'name' => $row_board['board_name'],
  197. 'description' => $row_board['description'],
  198. 'short_description' => shorten_subject(strip_tags($row_board['description']), 128),
  199. 'new' => empty($row_board['is_read']) && $row_board['poster_name'] != '',
  200. 'topics' => $row_board['num_topics'],
  201. 'posts' => $row_board['num_posts'],
  202. 'is_redirect' => $row_board['is_redirect'],
  203. 'unapproved_topics' => $row_board['unapproved_topics'],
  204. 'unapproved_posts' => $row_board['unapproved_posts'] - $row_board['unapproved_topics'],
  205. 'can_approve_posts' => !empty($user_info['mod_cache']['ap']) && ($user_info['mod_cache']['ap'] == array(0) || in_array($row_board['id_board'], $user_info['mod_cache']['ap'])),
  206. 'href' => $scripturl . '?board=' . $row_board['id_board'] . '.0',
  207. 'link' => '<a href="' . $scripturl . '?board=' . $row_board['id_board'] . '.0">' . $row_board['board_name'] . '</a>'
  208. );
  209. // Counting child board posts is... slow :/.
  210. if (!empty($boardIndexOptions['countChildPosts']) && !$row_board['is_redirect'])
  211. {
  212. $this_category[$row_board['id_parent']]['posts'] += $row_board['num_posts'];
  213. $this_category[$row_board['id_parent']]['topics'] += $row_board['num_topics'];
  214. }
  215. // Does this board contain new boards?
  216. $this_category[$row_board['id_parent']]['children_new'] |= empty($row_board['is_read']);
  217. // Update the icon if appropriate
  218. if ($this_category[$row_board['id_parent']]['children_new'] && $this_category[$row_board['id_parent']]['board_class'] == 'off')
  219. {
  220. $this_category[$row_board['id_parent']]['board_class'] = 'on2';
  221. $this_category[$row_board['id_parent']]['board_tooltip'] = $txt['new_posts'];
  222. }
  223. // This is easier to use in many cases for the theme....
  224. $this_category[$row_board['id_parent']]['link_children'][] = &$this_category[$row_board['id_parent']]['children'][$row_board['id_board']]['link'];
  225. }
  226. // Child of a child... just add it on...
  227. elseif (!empty($boardIndexOptions['countChildPosts']))
  228. {
  229. if (!isset($parent_map))
  230. $parent_map = array();
  231. if (!isset($parent_map[$row_board['id_parent']]))
  232. foreach ($this_category as $id => $board)
  233. {
  234. if (!isset($board['children'][$row_board['id_parent']]))
  235. continue;
  236. $parent_map[$row_board['id_parent']] = array(&$this_category[$id], &$this_category[$id]['children'][$row_board['id_parent']]);
  237. $parent_map[$row_board['id_board']] = array(&$this_category[$id], &$this_category[$id]['children'][$row_board['id_parent']]);
  238. break;
  239. }
  240. if (isset($parent_map[$row_board['id_parent']]) && !$row_board['is_redirect'])
  241. {
  242. $parent_map[$row_board['id_parent']][0]['posts'] += $row_board['num_posts'];
  243. $parent_map[$row_board['id_parent']][0]['topics'] += $row_board['num_topics'];
  244. $parent_map[$row_board['id_parent']][1]['posts'] += $row_board['num_posts'];
  245. $parent_map[$row_board['id_parent']][1]['topics'] += $row_board['num_topics'];
  246. continue;
  247. }
  248. continue;
  249. }
  250. // Found a child of a child - skip.
  251. else
  252. continue;
  253. // Prepare the subject, and make sure it's not too long.
  254. censorText($row_board['subject']);
  255. $row_board['short_subject'] = shorten_subject($row_board['subject'], 24);
  256. $this_last_post = array(
  257. 'id' => $row_board['id_msg'],
  258. 'time' => $row_board['poster_time'] > 0 ? timeformat($row_board['poster_time']) : $txt['not_applicable'],
  259. 'timestamp' => forum_time(true, $row_board['poster_time']),
  260. 'subject' => $row_board['short_subject'],
  261. 'member' => array(
  262. 'id' => $row_board['id_member'],
  263. 'username' => $row_board['poster_name'] != '' ? $row_board['poster_name'] : $txt['not_applicable'],
  264. 'name' => $row_board['real_name'],
  265. 'href' => $row_board['poster_name'] != '' && !empty($row_board['id_member']) ? $scripturl . '?action=profile;u=' . $row_board['id_member'] : '',
  266. 'link' => $row_board['poster_name'] != '' ? (!empty($row_board['id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row_board['id_member'] . '">' . $row_board['real_name'] . '</a>' : $row_board['real_name']) : $txt['not_applicable'],
  267. ),
  268. 'start' => 'msg' . $row_board['new_from'],
  269. 'topic' => $row_board['id_topic']
  270. );
  271. if (!empty($settings['avatars_on_indexes']))
  272. $this_last_post['member']['avatar'] = array(
  273. 'name' => $row_board['avatar'],
  274. 'image' => $row_board['avatar'] == '' ? ($row_board['id_attach'] > 0 ? '<img class="avatar" src="' . (empty($row_board['attachment_type']) ? $scripturl . '?action=dlattach;attach=' . $row_board['id_attach'] . ';type=avatar' : $modSettings['custom_avatar_url'] . '/' . $row_board['filename']) . '" alt="">' : '') : (stristr($row_board['avatar'], 'http://') || stristr($row_board['avatar'], 'https://') ? '<img class="avatar" src="' . $row_board['avatar'] . '" alt="">' : '<img class="avatar" src="' . $modSettings['avatar_url'] . '/' . $smcFunc['htmlspecialchars']($row_board['avatar']) . '" alt="">'),
  275. 'href' => $row_board['avatar'] == '' ? ($row_board['id_attach'] > 0 ? (empty($row_board['attachment_type']) ? $scripturl . '?action=dlattach;attach=' . $row_board['id_attach'] . ';type=avatar' : $modSettings['custom_avatar_url'] . '/' . $row_board['filename']) : '') : (stristr($row_board['avatar'], 'http://') || stristr($row_board['avatar'], 'https://') ? $row_board['avatar'] : $modSettings['avatar_url'] . '/' . $row_board['avatar']),
  276. 'url' => $row_board['avatar'] == '' ? '' : (stristr($row_board['avatar'], 'http://') || stristr($row_board['avatar'], 'https://') ? $row_board['avatar'] : $modSettings['avatar_url'] . '/' . $row_board['avatar'])
  277. );
  278. // Provide the href and link.
  279. if ($row_board['subject'] != '')
  280. {
  281. $this_last_post['href'] = $scripturl . '?topic=' . $row_board['id_topic'] . '.msg' . ($user_info['is_guest'] ? $row_board['id_msg'] : $row_board['new_from']) . (empty($row_board['is_read']) ? ';boardseen' : '') . '#new';
  282. $this_last_post['link'] = '<a href="' . $this_last_post['href'] . '" title="' . $row_board['subject'] . '">' . $row_board['short_subject'] . '</a>';
  283. /* The board's and children's 'last_post's have:
  284. time, timestamp (a number that represents the time.), id (of the post), topic (topic id.),
  285. link, href, subject, start (where they should go for the first unread post.),
  286. and member. (which has id, name, link, href, username in it.) */
  287. $this_last_post['last_post_message'] = sprintf($txt['last_post_message'], $this_last_post['member']['link'], $this_last_post['link'], $this_last_post['time']);
  288. }
  289. else
  290. {
  291. $this_last_post['href'] = '';
  292. $this_last_post['link'] = $txt['not_applicable'];
  293. $this_last_post['last_post_message'] = '';
  294. }
  295. // Set the last post in the parent board.
  296. if ($row_board['id_parent'] == $boardIndexOptions['parent_id'] || ($isChild && !empty($row_board['poster_time']) && $this_category[$row_board['id_parent']]['last_post']['timestamp'] < forum_time(true, $row_board['poster_time'])))
  297. $this_category[$isChild ? $row_board['id_parent'] : $row_board['id_board']]['last_post'] = $this_last_post;
  298. // Just in the child...?
  299. if ($isChild)
  300. {
  301. $this_category[$row_board['id_parent']]['children'][$row_board['id_board']]['last_post'] = $this_last_post;
  302. // If there are no posts in this board, it really can't be new...
  303. $this_category[$row_board['id_parent']]['children'][$row_board['id_board']]['new'] &= $row_board['poster_name'] != '';
  304. }
  305. // No last post for this board? It's not new then, is it..?
  306. elseif ($row_board['poster_name'] == '')
  307. $this_category[$row_board['id_board']]['new'] = false;
  308. // Determine a global most recent topic.
  309. if (!empty($boardIndexOptions['set_latest_post']) && !empty($row_board['poster_time']) && $row_board['poster_time'] > $latest_post['timestamp'] && !$ignoreThisBoard)
  310. $latest_post = array(
  311. 'timestamp' => $row_board['poster_time'],
  312. 'ref' => &$this_category[$isChild ? $row_board['id_parent'] : $row_board['id_board']]['last_post'],
  313. );
  314. }
  315. $smcFunc['db_free_result']($result_boards);
  316. // By now we should know the most recent post...if we wanna know it that is.
  317. if (!empty($boardIndexOptions['set_latest_post']) && !empty($latest_post['ref']))
  318. $context['latest_post'] = $latest_post['ref'];
  319. // I can't remember why but trying to make a ternary to get this all in one line is actually a Very Bad Idea.
  320. if ($boardIndexOptions['include_categories'])
  321. call_integration_hook('integrate_getboardtree', array($boardIndexOptions, &$categories));
  322. else
  323. call_integration_hook('integrate_getboardtree', array($boardIndexOptions, &$this_category));
  324. return $boardIndexOptions['include_categories'] ? $categories : $this_category;
  325. }
  326. ?>