Subs-Boards.php 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181
  1. <?php
  2. /**
  3. * Simple Machines Forum (SMF)
  4. *
  5. * @package SMF
  6. * @author Simple Machines http://www.simplemachines.org
  7. * @copyright 2011 Simple Machines
  8. * @license http://www.simplemachines.org/about/smf/license.php BSD
  9. *
  10. * @version 2.1 Alpha 1
  11. */
  12. if (!defined('SMF'))
  13. die('Hacking attempt...');
  14. /* This file is mainly concerned with minor tasks relating to boards, such as
  15. marking them read, collapsing categories, or quick moderation. It defines
  16. the following list of functions:
  17. void MarkRead()
  18. // @todo
  19. int getMsgMemberID(int id_msg)
  20. // @todo
  21. void modifyBoard(int board_id, array boardOptions)
  22. - general function to modify the settings and position of a board.
  23. - used by ManageBoards.php to change the settings of a board.
  24. int createBoard(array boardOptions)
  25. - general function to create a new board and set its position.
  26. - allows (almost) the same options as the modifyBoard() function.
  27. - with the option inherit_permissions set, the parent board permissions
  28. will be inherited.
  29. - returns the ID of the newly created board.
  30. void deleteBoards(array boards_to_remove, moveChildrenTo = null)
  31. - general function to delete one or more boards.
  32. - allows to move the children of the board before deleting it
  33. - if moveChildrenTo is set to null, the child boards will be deleted.
  34. - deletes all topics that are on the given boards.
  35. - deletes all information that's associated with the given boards.
  36. - updates the statistics to reflect the new situation.
  37. void reorderBoards()
  38. - updates the database to put all boards in the right order.
  39. - sorts the records of the boards table.
  40. - used by modifyBoard(), deleteBoards(), modifyCategory(), and
  41. deleteCategories() functions.
  42. void fixChildren(int parent, int newLevel, int newParent)
  43. - recursively updates the children of parent's child_level and
  44. id_parent to newLevel and newParent.
  45. - used when a board is deleted or moved, to affect its children.
  46. bool isChildOf(int child, int parent)
  47. - determines if child is a child of parent.
  48. - recurses down the tree until there are no more parents.
  49. - returns true if child is a child of parent.
  50. void getBoardTree()
  51. - load information regarding the boards and categories.
  52. - the information retrieved is stored in globals:
  53. - $boards properties of each board.
  54. - $boardList a list of boards grouped by category ID.
  55. - $cat_tree properties of each category.
  56. void recursiveBoards()
  57. - function used by getBoardTree to recursively get a list of boards.
  58. bool isChildOf(int child, int parent)
  59. - determine if a certain board id is a child of another board.
  60. - the parent might be several levels higher than the child.
  61. */
  62. /**
  63. * Mark a board or multiple boards read.
  64. *
  65. * @param array $boards
  66. * @param bool $unread
  67. */
  68. function markBoardsRead($boards, $unread = false)
  69. {
  70. global $user_info, $modSettings, $smcFunc;
  71. // Force $boards to be an array.
  72. if (!is_array($boards))
  73. $boards = array($boards);
  74. else
  75. $boards = array_unique($boards);
  76. // No boards, nothing to mark as read.
  77. if (empty($boards))
  78. return;
  79. // Allow the user to mark a board as unread.
  80. if ($unread)
  81. {
  82. // Clear out all the places where this lovely info is stored.
  83. // !! Maybe not log_mark_read?
  84. $smcFunc['db_query']('', '
  85. DELETE FROM {db_prefix}log_mark_read
  86. WHERE id_board IN ({array_int:board_list})
  87. AND id_member = {int:current_member}',
  88. array(
  89. 'current_member' => $user_info['id'],
  90. 'board_list' => $boards,
  91. )
  92. );
  93. $smcFunc['db_query']('', '
  94. DELETE FROM {db_prefix}log_boards
  95. WHERE id_board IN ({array_int:board_list})
  96. AND id_member = {int:current_member}',
  97. array(
  98. 'current_member' => $user_info['id'],
  99. 'board_list' => $boards,
  100. )
  101. );
  102. }
  103. // Otherwise mark the board as read.
  104. else
  105. {
  106. $markRead = array();
  107. foreach ($boards as $board)
  108. $markRead[] = array($modSettings['maxMsgID'], $user_info['id'], $board);
  109. // Update log_mark_read and log_boards.
  110. $smcFunc['db_insert']('replace',
  111. '{db_prefix}log_mark_read',
  112. array('id_msg' => 'int', 'id_member' => 'int', 'id_board' => 'int'),
  113. $markRead,
  114. array('id_board', 'id_member')
  115. );
  116. $smcFunc['db_insert']('replace',
  117. '{db_prefix}log_boards',
  118. array('id_msg' => 'int', 'id_member' => 'int', 'id_board' => 'int'),
  119. $markRead,
  120. array('id_board', 'id_member')
  121. );
  122. }
  123. // Get rid of useless log_topics data, because log_mark_read is better for it - even if marking unread - I think so...
  124. // @todo look at this...
  125. // The call to markBoardsRead() in Display() used to be simply
  126. // marking log_boards (the previous query only)
  127. $result = $smcFunc['db_query']('', '
  128. SELECT MIN(id_topic)
  129. FROM {db_prefix}log_topics
  130. WHERE id_member = {int:current_member}',
  131. array(
  132. 'current_member' => $user_info['id'],
  133. )
  134. );
  135. list ($lowest_topic) = $smcFunc['db_fetch_row']($result);
  136. $smcFunc['db_free_result']($result);
  137. if (empty($lowest_topic))
  138. return;
  139. // @todoSLOW This query seems to eat it sometimes.
  140. $result = $smcFunc['db_query']('', '
  141. SELECT lt.id_topic
  142. FROM {db_prefix}log_topics AS lt
  143. INNER JOIN {db_prefix}topics AS t /*!40000 USE INDEX (PRIMARY) */ ON (t.id_topic = lt.id_topic
  144. AND t.id_board IN ({array_int:board_list}))
  145. WHERE lt.id_member = {int:current_member}
  146. AND lt.id_topic >= {int:lowest_topic}',
  147. array(
  148. 'current_member' => $user_info['id'],
  149. 'board_list' => $boards,
  150. 'lowest_topic' => $lowest_topic,
  151. )
  152. );
  153. $topics = array();
  154. while ($row = $smcFunc['db_fetch_assoc']($result))
  155. $topics[] = $row['id_topic'];
  156. $smcFunc['db_free_result']($result);
  157. if (!empty($topics))
  158. $smcFunc['db_query']('', '
  159. DELETE FROM {db_prefix}log_topics
  160. WHERE id_member = {int:current_member}
  161. AND id_topic IN ({array_int:topic_list})',
  162. array(
  163. 'current_member' => $user_info['id'],
  164. 'topic_list' => $topics,
  165. )
  166. );
  167. }
  168. // Mark one or more boards as read.
  169. function MarkRead()
  170. {
  171. global $board, $topic, $user_info, $board_info, $modSettings, $smcFunc;
  172. // No Guests allowed!
  173. is_not_guest();
  174. checkSession('get');
  175. if (isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'all')
  176. {
  177. // Find all the boards this user can see.
  178. $result = $smcFunc['db_query']('', '
  179. SELECT b.id_board
  180. FROM {db_prefix}boards AS b
  181. WHERE {query_see_board}',
  182. array(
  183. )
  184. );
  185. $boards = array();
  186. while ($row = $smcFunc['db_fetch_assoc']($result))
  187. $boards[] = $row['id_board'];
  188. $smcFunc['db_free_result']($result);
  189. if (!empty($boards))
  190. markBoardsRead($boards, isset($_REQUEST['unread']));
  191. $_SESSION['id_msg_last_visit'] = $modSettings['maxMsgID'];
  192. if (!empty($_SESSION['old_url']) && strpos($_SESSION['old_url'], 'action=unread') !== false)
  193. redirectexit('action=unread');
  194. if (isset($_SESSION['topicseen_cache']))
  195. $_SESSION['topicseen_cache'] = array();
  196. redirectexit();
  197. }
  198. elseif (isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'unreadreplies')
  199. {
  200. // Make sure all the boards are integers!
  201. $topics = explode('-', $_REQUEST['topics']);
  202. $markRead = array();
  203. foreach ($topics as $id_topic)
  204. $markRead[] = array($modSettings['maxMsgID'], $user_info['id'], (int) $id_topic);
  205. $smcFunc['db_insert']('replace',
  206. '{db_prefix}log_topics',
  207. array('id_msg' => 'int', 'id_member' => 'int', 'id_topic' => 'int'),
  208. $markRead,
  209. array('id_member', 'id_topic')
  210. );
  211. if (isset($_SESSION['topicseen_cache']))
  212. $_SESSION['topicseen_cache'] = array();
  213. redirectexit('action=unreadreplies');
  214. }
  215. // Special case: mark a topic unread!
  216. elseif (isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'topic')
  217. {
  218. // First, let's figure out what the latest message is.
  219. $result = $smcFunc['db_query']('', '
  220. SELECT id_first_msg, id_last_msg
  221. FROM {db_prefix}topics
  222. WHERE id_topic = {int:current_topic}',
  223. array(
  224. 'current_topic' => $topic,
  225. )
  226. );
  227. $topicinfo = $smcFunc['db_fetch_assoc']($result);
  228. $smcFunc['db_free_result']($result);
  229. if (!empty($_GET['t']))
  230. {
  231. // If they read the whole topic, go back to the beginning.
  232. if ($_GET['t'] >= $topicinfo['id_last_msg'])
  233. $earlyMsg = 0;
  234. // If they want to mark the whole thing read, same.
  235. elseif ($_GET['t'] <= $topicinfo['id_first_msg'])
  236. $earlyMsg = 0;
  237. // Otherwise, get the latest message before the named one.
  238. else
  239. {
  240. $result = $smcFunc['db_query']('', '
  241. SELECT MAX(id_msg)
  242. FROM {db_prefix}messages
  243. WHERE id_topic = {int:current_topic}
  244. AND id_msg >= {int:id_first_msg}
  245. AND id_msg < {int:topic_msg_id}',
  246. array(
  247. 'current_topic' => $topic,
  248. 'topic_msg_id' => (int) $_GET['t'],
  249. 'id_first_msg' => $topicinfo['id_first_msg'],
  250. )
  251. );
  252. list ($earlyMsg) = $smcFunc['db_fetch_row']($result);
  253. $smcFunc['db_free_result']($result);
  254. }
  255. }
  256. // Marking read from first page? That's the whole topic.
  257. elseif ($_REQUEST['start'] == 0)
  258. $earlyMsg = 0;
  259. else
  260. {
  261. $result = $smcFunc['db_query']('', '
  262. SELECT id_msg
  263. FROM {db_prefix}messages
  264. WHERE id_topic = {int:current_topic}
  265. ORDER BY id_msg
  266. LIMIT ' . (int) $_REQUEST['start'] . ', 1',
  267. array(
  268. 'current_topic' => $topic,
  269. )
  270. );
  271. list ($earlyMsg) = $smcFunc['db_fetch_row']($result);
  272. $smcFunc['db_free_result']($result);
  273. $earlyMsg--;
  274. }
  275. // Blam, unread!
  276. $smcFunc['db_insert']('replace',
  277. '{db_prefix}log_topics',
  278. array('id_msg' => 'int', 'id_member' => 'int', 'id_topic' => 'int'),
  279. array($earlyMsg, $user_info['id'], $topic),
  280. array('id_member', 'id_topic')
  281. );
  282. redirectexit('board=' . $board . '.0');
  283. }
  284. else
  285. {
  286. $categories = array();
  287. $boards = array();
  288. if (isset($_REQUEST['c']))
  289. {
  290. $_REQUEST['c'] = explode(',', $_REQUEST['c']);
  291. foreach ($_REQUEST['c'] as $c)
  292. $categories[] = (int) $c;
  293. }
  294. if (isset($_REQUEST['boards']))
  295. {
  296. $_REQUEST['boards'] = explode(',', $_REQUEST['boards']);
  297. foreach ($_REQUEST['boards'] as $b)
  298. $boards[] = (int) $b;
  299. }
  300. if (!empty($board))
  301. $boards[] = (int) $board;
  302. if (isset($_REQUEST['children']) && !empty($boards))
  303. {
  304. // They want to mark the entire tree starting with the boards specified
  305. // The easist thing is to just get all the boards they can see, but since we've specified the top of tree we ignore some of them
  306. $request = $smcFunc['db_query']('', '
  307. SELECT b.id_board, b.id_parent
  308. FROM {db_prefix}boards AS b
  309. WHERE {query_see_board}
  310. AND b.child_level > {int:no_parents}
  311. AND b.id_board NOT IN ({array_int:board_list})
  312. ORDER BY child_level ASC
  313. ',
  314. array(
  315. 'no_parents' => 0,
  316. 'board_list' => $boards,
  317. )
  318. );
  319. while ($row = $smcFunc['db_fetch_assoc']($request))
  320. if (in_array($row['id_parent'], $boards))
  321. $boards[] = $row['id_board'];
  322. $smcFunc['db_free_result']($request);
  323. }
  324. $clauses = array();
  325. $clauseParameters = array();
  326. if (!empty($categories))
  327. {
  328. $clauses[] = 'id_cat IN ({array_int:category_list})';
  329. $clauseParameters['category_list'] = $categories;
  330. }
  331. if (!empty($boards))
  332. {
  333. $clauses[] = 'id_board IN ({array_int:board_list})';
  334. $clauseParameters['board_list'] = $boards;
  335. }
  336. if (empty($clauses))
  337. redirectexit();
  338. $request = $smcFunc['db_query']('', '
  339. SELECT b.id_board
  340. FROM {db_prefix}boards AS b
  341. WHERE {query_see_board}
  342. AND b.' . implode(' OR b.', $clauses),
  343. array_merge($clauseParameters, array(
  344. ))
  345. );
  346. $boards = array();
  347. while ($row = $smcFunc['db_fetch_assoc']($request))
  348. $boards[] = $row['id_board'];
  349. $smcFunc['db_free_result']($request);
  350. if (empty($boards))
  351. redirectexit();
  352. markBoardsRead($boards, isset($_REQUEST['unread']));
  353. foreach ($boards as $b)
  354. {
  355. if (isset($_SESSION['topicseen_cache'][$b]))
  356. $_SESSION['topicseen_cache'][$b] = array();
  357. }
  358. if (!isset($_REQUEST['unread']))
  359. {
  360. // Find all the boards this user can see.
  361. $result = $smcFunc['db_query']('', '
  362. SELECT b.id_board
  363. FROM {db_prefix}boards AS b
  364. WHERE b.id_parent IN ({array_int:parent_list})
  365. AND {query_see_board}',
  366. array(
  367. 'parent_list' => $boards,
  368. )
  369. );
  370. if ($smcFunc['db_num_rows']($result) > 0)
  371. {
  372. $logBoardInserts = '';
  373. while ($row = $smcFunc['db_fetch_assoc']($result))
  374. $logBoardInserts[] = array($modSettings['maxMsgID'], $user_info['id'], $row['id_board']);
  375. $smcFunc['db_insert']('replace',
  376. '{db_prefix}log_boards',
  377. array('id_msg' => 'int', 'id_member' => 'int', 'id_board' => 'int'),
  378. $logBoardInserts,
  379. array('id_member', 'id_board')
  380. );
  381. }
  382. $smcFunc['db_free_result']($result);
  383. if (empty($board))
  384. redirectexit();
  385. else
  386. redirectexit('board=' . $board . '.0');
  387. }
  388. else
  389. {
  390. if (empty($board_info['parent']))
  391. redirectexit();
  392. else
  393. redirectexit('board=' . $board_info['parent'] . '.0');
  394. }
  395. }
  396. }
  397. // Get the id_member associated with the specified message.
  398. function getMsgMemberID($messageID)
  399. {
  400. global $smcFunc;
  401. // Find the topic and make sure the member still exists.
  402. $result = $smcFunc['db_query']('', '
  403. SELECT IFNULL(mem.id_member, 0)
  404. FROM {db_prefix}messages AS m
  405. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
  406. WHERE m.id_msg = {int:selected_message}
  407. LIMIT 1',
  408. array(
  409. 'selected_message' => (int) $messageID,
  410. )
  411. );
  412. if ($smcFunc['db_num_rows']($result) > 0)
  413. list ($memberID) = $smcFunc['db_fetch_row']($result);
  414. // The message doesn't even exist.
  415. else
  416. $memberID = 0;
  417. $smcFunc['db_free_result']($result);
  418. return (int) $memberID;
  419. }
  420. // Modify the settings and position of a board.
  421. function modifyBoard($board_id, &$boardOptions)
  422. {
  423. global $sourcedir, $cat_tree, $boards, $boardList, $modSettings, $smcFunc;
  424. // Get some basic information about all boards and categories.
  425. getBoardTree();
  426. // Make sure given boards and categories exist.
  427. if (!isset($boards[$board_id]) || (isset($boardOptions['target_board']) && !isset($boards[$boardOptions['target_board']])) || (isset($boardOptions['target_category']) && !isset($cat_tree[$boardOptions['target_category']])))
  428. fatal_lang_error('no_board');
  429. call_integration_hook('integrate_modify_board', array($board_id, &$boardOptions));
  430. // All things that will be updated in the database will be in $boardUpdates.
  431. $boardUpdates = array();
  432. $boardUpdateParameters = array();
  433. // In case the board has to be moved
  434. if (isset($boardOptions['move_to']))
  435. {
  436. // Move the board to the top of a given category.
  437. if ($boardOptions['move_to'] == 'top')
  438. {
  439. $id_cat = $boardOptions['target_category'];
  440. $child_level = 0;
  441. $id_parent = 0;
  442. $after = $cat_tree[$id_cat]['last_board_order'];
  443. }
  444. // Move the board to the bottom of a given category.
  445. elseif ($boardOptions['move_to'] == 'bottom')
  446. {
  447. $id_cat = $boardOptions['target_category'];
  448. $child_level = 0;
  449. $id_parent = 0;
  450. $after = 0;
  451. foreach ($cat_tree[$id_cat]['children'] as $id_board => $dummy)
  452. $after = max($after, $boards[$id_board]['order']);
  453. }
  454. // Make the board a child of a given board.
  455. elseif ($boardOptions['move_to'] == 'child')
  456. {
  457. $id_cat = $boards[$boardOptions['target_board']]['category'];
  458. $child_level = $boards[$boardOptions['target_board']]['level'] + 1;
  459. $id_parent = $boardOptions['target_board'];
  460. // People can be creative, in many ways...
  461. if (isChildOf($id_parent, $board_id))
  462. fatal_lang_error('mboards_parent_own_child_error', false);
  463. elseif ($id_parent == $board_id)
  464. fatal_lang_error('mboards_board_own_child_error', false);
  465. $after = $boards[$boardOptions['target_board']]['order'];
  466. // Check if there are already children and (if so) get the max board order.
  467. if (!empty($boards[$id_parent]['tree']['children']) && empty($boardOptions['move_first_child']))
  468. foreach ($boards[$id_parent]['tree']['children'] as $childBoard_id => $dummy)
  469. $after = max($after, $boards[$childBoard_id]['order']);
  470. }
  471. // Place a board before or after another board, on the same child level.
  472. elseif (in_array($boardOptions['move_to'], array('before', 'after')))
  473. {
  474. $id_cat = $boards[$boardOptions['target_board']]['category'];
  475. $child_level = $boards[$boardOptions['target_board']]['level'];
  476. $id_parent = $boards[$boardOptions['target_board']]['parent'];
  477. $after = $boards[$boardOptions['target_board']]['order'] - ($boardOptions['move_to'] == 'before' ? 1 : 0);
  478. }
  479. // Oops...?
  480. else
  481. trigger_error('modifyBoard(): The move_to value \'' . $boardOptions['move_to'] . '\' is incorrect', E_USER_ERROR);
  482. // Get a list of children of this board.
  483. $childList = array();
  484. recursiveBoards($childList, $boards[$board_id]['tree']);
  485. // See if there are changes that affect children.
  486. $childUpdates = array();
  487. $levelDiff = $child_level - $boards[$board_id]['level'];
  488. if ($levelDiff != 0)
  489. $childUpdates[] = 'child_level = child_level ' . ($levelDiff > 0 ? '+ ' : '') . '{int:level_diff}';
  490. if ($id_cat != $boards[$board_id]['category'])
  491. $childUpdates[] = 'id_cat = {int:category}';
  492. // Fix the children of this board.
  493. if (!empty($childList) && !empty($childUpdates))
  494. $smcFunc['db_query']('', '
  495. UPDATE {db_prefix}boards
  496. SET ' . implode(',
  497. ', $childUpdates) . '
  498. WHERE id_board IN ({array_int:board_list})',
  499. array(
  500. 'board_list' => $childList,
  501. 'category' => $id_cat,
  502. 'level_diff' => $levelDiff,
  503. )
  504. );
  505. // Make some room for this spot.
  506. $smcFunc['db_query']('', '
  507. UPDATE {db_prefix}boards
  508. SET board_order = board_order + {int:new_order}
  509. WHERE board_order > {int:insert_after}
  510. AND id_board != {int:selected_board}',
  511. array(
  512. 'insert_after' => $after,
  513. 'selected_board' => $board_id,
  514. 'new_order' => 1 + count($childList),
  515. )
  516. );
  517. $boardUpdates[] = 'id_cat = {int:id_cat}';
  518. $boardUpdates[] = 'id_parent = {int:id_parent}';
  519. $boardUpdates[] = 'child_level = {int:child_level}';
  520. $boardUpdates[] = 'board_order = {int:board_order}';
  521. $boardUpdateParameters += array(
  522. 'id_cat' => $id_cat,
  523. 'id_parent' => $id_parent,
  524. 'child_level' => $child_level,
  525. 'board_order' => $after + 1,
  526. );
  527. }
  528. // This setting is a little twisted in the database...
  529. if (isset($boardOptions['posts_count']))
  530. {
  531. $boardUpdates[] = 'count_posts = {int:count_posts}';
  532. $boardUpdateParameters['count_posts'] = $boardOptions['posts_count'] ? 0 : 1;
  533. }
  534. // Set the theme for this board.
  535. if (isset($boardOptions['board_theme']))
  536. {
  537. $boardUpdates[] = 'id_theme = {int:id_theme}';
  538. $boardUpdateParameters['id_theme'] = (int) $boardOptions['board_theme'];
  539. }
  540. // Should the board theme override the user preferred theme?
  541. if (isset($boardOptions['override_theme']))
  542. {
  543. $boardUpdates[] = 'override_theme = {int:override_theme}';
  544. $boardUpdateParameters['override_theme'] = $boardOptions['override_theme'] ? 1 : 0;
  545. }
  546. // Who's allowed to access this board.
  547. if (isset($boardOptions['access_groups']))
  548. {
  549. $boardUpdates[] = 'member_groups = {string:member_groups}';
  550. $boardUpdateParameters['member_groups'] = implode(',', $boardOptions['access_groups']);
  551. }
  552. if (isset($boardOptions['board_name']))
  553. {
  554. $boardUpdates[] = 'name = {string:board_name}';
  555. $boardUpdateParameters['board_name'] = $boardOptions['board_name'];
  556. }
  557. if (isset($boardOptions['board_description']))
  558. {
  559. $boardUpdates[] = 'description = {string:board_description}';
  560. $boardUpdateParameters['board_description'] = $boardOptions['board_description'];
  561. }
  562. if (isset($boardOptions['profile']))
  563. {
  564. $boardUpdates[] = 'id_profile = {int:profile}';
  565. $boardUpdateParameters['profile'] = (int) $boardOptions['profile'];
  566. }
  567. if (isset($boardOptions['redirect']))
  568. {
  569. $boardUpdates[] = 'redirect = {string:redirect}';
  570. $boardUpdateParameters['redirect'] = $boardOptions['redirect'];
  571. }
  572. if (isset($boardOptions['num_posts']))
  573. {
  574. $boardUpdates[] = 'num_posts = {int:num_posts}';
  575. $boardUpdateParameters['num_posts'] = (int) $boardOptions['num_posts'];
  576. }
  577. // Do the updates (if any).
  578. if (!empty($boardUpdates))
  579. $request = $smcFunc['db_query']('', '
  580. UPDATE {db_prefix}boards
  581. SET
  582. ' . implode(',
  583. ', $boardUpdates) . '
  584. WHERE id_board = {int:selected_board}',
  585. array_merge($boardUpdateParameters, array(
  586. 'selected_board' => $board_id,
  587. ))
  588. );
  589. // Set moderators of this board.
  590. if (isset($boardOptions['moderators']) || isset($boardOptions['moderator_string']))
  591. {
  592. // Reset current moderators for this board - if there are any!
  593. $smcFunc['db_query']('', '
  594. DELETE FROM {db_prefix}moderators
  595. WHERE id_board = {int:board_list}',
  596. array(
  597. 'board_list' => $board_id,
  598. )
  599. );
  600. // Validate and get the IDs of the new moderators.
  601. if (isset($boardOptions['moderator_string']) && trim($boardOptions['moderator_string']) != '')
  602. {
  603. // Divvy out the usernames, remove extra space.
  604. $moderator_string = strtr($smcFunc['htmlspecialchars']($boardOptions['moderator_string'], ENT_QUOTES), array('&quot;' => '"'));
  605. preg_match_all('~"([^"]+)"~', $moderator_string, $matches);
  606. $moderators = array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $moderator_string)));
  607. for ($k = 0, $n = count($moderators); $k < $n; $k++)
  608. {
  609. $moderators[$k] = trim($moderators[$k]);
  610. if (strlen($moderators[$k]) == 0)
  611. unset($moderators[$k]);
  612. }
  613. // Find all the id_member's for the member_name's in the list.
  614. if (empty($boardOptions['moderators']))
  615. $boardOptions['moderators'] = array();
  616. if (!empty($moderators))
  617. {
  618. $request = $smcFunc['db_query']('', '
  619. SELECT id_member
  620. FROM {db_prefix}members
  621. WHERE member_name IN ({array_string:moderator_list}) OR real_name IN ({array_string:moderator_list})
  622. LIMIT ' . count($moderators),
  623. array(
  624. 'moderator_list' => $moderators,
  625. )
  626. );
  627. while ($row = $smcFunc['db_fetch_assoc']($request))
  628. $boardOptions['moderators'][] = $row['id_member'];
  629. $smcFunc['db_free_result']($request);
  630. }
  631. }
  632. // Add the moderators to the board.
  633. if (!empty($boardOptions['moderators']))
  634. {
  635. $inserts = array();
  636. foreach ($boardOptions['moderators'] as $moderator)
  637. $inserts[] = array($board_id, $moderator);
  638. $smcFunc['db_insert']('insert',
  639. '{db_prefix}moderators',
  640. array('id_board' => 'int', 'id_member' => 'int'),
  641. $inserts,
  642. array('id_board', 'id_member')
  643. );
  644. }
  645. // Note that caches can now be wrong!
  646. updateSettings(array('settings_updated' => time()));
  647. }
  648. if (isset($boardOptions['move_to']))
  649. reorderBoards();
  650. clean_cache('data');
  651. if (empty($boardOptions['dont_log']))
  652. logAction('edit_board', array('board' => $board_id), 'admin');
  653. }
  654. // Create a new board and set its properties and position.
  655. function createBoard($boardOptions)
  656. {
  657. global $boards, $modSettings, $smcFunc;
  658. // Trigger an error if one of the required values is not set.
  659. if (!isset($boardOptions['board_name']) || trim($boardOptions['board_name']) == '' || !isset($boardOptions['move_to']) || !isset($boardOptions['target_category']))
  660. trigger_error('createBoard(): One or more of the required options is not set', E_USER_ERROR);
  661. if (in_array($boardOptions['move_to'], array('child', 'before', 'after')) && !isset($boardOptions['target_board']))
  662. trigger_error('createBoard(): Target board is not set', E_USER_ERROR);
  663. call_integration_hook('integrate_create_board', array(&$boardOptions));
  664. // Set every optional value to its default value.
  665. $boardOptions += array(
  666. 'posts_count' => true,
  667. 'override_theme' => false,
  668. 'board_theme' => 0,
  669. 'access_groups' => array(),
  670. 'board_description' => '',
  671. 'profile' => 1,
  672. 'moderators' => '',
  673. 'inherit_permissions' => true,
  674. 'dont_log' => true,
  675. );
  676. // Insert a board, the settings are dealt with later.
  677. $smcFunc['db_insert']('',
  678. '{db_prefix}boards',
  679. array(
  680. 'id_cat' => 'int', 'name' => 'string-255', 'description' => 'string', 'board_order' => 'int',
  681. 'member_groups' => 'string', 'redirect' => 'string',
  682. ),
  683. array(
  684. $boardOptions['target_category'], $boardOptions['board_name'] , '', 0,
  685. '-1,0', '',
  686. ),
  687. array('id_board')
  688. );
  689. $board_id = $smcFunc['db_insert_id']('{db_prefix}boards', 'id_board');
  690. if (empty($board_id))
  691. return 0;
  692. // Change the board according to the given specifications.
  693. modifyBoard($board_id, $boardOptions);
  694. // Do we want the parent permissions to be inherited?
  695. if ($boardOptions['inherit_permissions'])
  696. {
  697. getBoardTree();
  698. if (!empty($boards[$board_id]['parent']))
  699. {
  700. $request = $smcFunc['db_query']('', '
  701. SELECT id_profile
  702. FROM {db_prefix}boards
  703. WHERE id_board = {int:board_parent}
  704. LIMIT 1',
  705. array(
  706. 'board_parent' => (int) $boards[$board_id]['parent'],
  707. )
  708. );
  709. list ($boardOptions['profile']) = $smcFunc['db_fetch_row']($request);
  710. $smcFunc['db_free_result']($request);
  711. $smcFunc['db_query']('', '
  712. UPDATE {db_prefix}boards
  713. SET id_profile = {int:new_profile}
  714. WHERE id_board = {int:current_board}',
  715. array(
  716. 'new_profile' => $boardOptions['profile'],
  717. 'current_board' => $board_id,
  718. )
  719. );
  720. }
  721. }
  722. // Clean the data cache.
  723. clean_cache('data');
  724. // Created it.
  725. logAction('add_board', array('board' => $board_id), 'admin');
  726. // Here you are, a new board, ready to be spammed.
  727. return $board_id;
  728. }
  729. // Remove one or more boards.
  730. function deleteBoards($boards_to_remove, $moveChildrenTo = null)
  731. {
  732. global $sourcedir, $boards, $smcFunc;
  733. // No boards to delete? Return!
  734. if (empty($boards_to_remove))
  735. return;
  736. getBoardTree();
  737. call_integration_hook('integrate_delete_board', array($boards_to_remove, &$moveChildrenTo));
  738. // If $moveChildrenTo is set to null, include the children in the removal.
  739. if ($moveChildrenTo === null)
  740. {
  741. // Get a list of the child boards that will also be removed.
  742. $child_boards_to_remove = array();
  743. foreach ($boards_to_remove as $board_to_remove)
  744. recursiveBoards($child_boards_to_remove, $boards[$board_to_remove]['tree']);
  745. // Merge the children with their parents.
  746. if (!empty($child_boards_to_remove))
  747. $boards_to_remove = array_unique(array_merge($boards_to_remove, $child_boards_to_remove));
  748. }
  749. // Move the children to a safe home.
  750. else
  751. {
  752. foreach ($boards_to_remove as $id_board)
  753. {
  754. // @todo Separate category?
  755. if ($moveChildrenTo === 0)
  756. fixChildren($id_board, 0, 0);
  757. else
  758. fixChildren($id_board, $boards[$moveChildrenTo]['level'] + 1, $moveChildrenTo);
  759. }
  760. }
  761. // Delete ALL topics in the selected boards (done first so topics can't be marooned.)
  762. $request = $smcFunc['db_query']('', '
  763. SELECT id_topic
  764. FROM {db_prefix}topics
  765. WHERE id_board IN ({array_int:boards_to_remove})',
  766. array(
  767. 'boards_to_remove' => $boards_to_remove,
  768. )
  769. );
  770. $topics = array();
  771. while ($row = $smcFunc['db_fetch_assoc']($request))
  772. $topics[] = $row['id_topic'];
  773. $smcFunc['db_free_result']($request);
  774. require_once($sourcedir . '/RemoveTopic.php');
  775. removeTopics($topics, false);
  776. // Delete the board's logs.
  777. $smcFunc['db_query']('', '
  778. DELETE FROM {db_prefix}log_mark_read
  779. WHERE id_board IN ({array_int:boards_to_remove})',
  780. array(
  781. 'boards_to_remove' => $boards_to_remove,
  782. )
  783. );
  784. $smcFunc['db_query']('', '
  785. DELETE FROM {db_prefix}log_boards
  786. WHERE id_board IN ({array_int:boards_to_remove})',
  787. array(
  788. 'boards_to_remove' => $boards_to_remove,
  789. )
  790. );
  791. $smcFunc['db_query']('', '
  792. DELETE FROM {db_prefix}log_notify
  793. WHERE id_board IN ({array_int:boards_to_remove})',
  794. array(
  795. 'boards_to_remove' => $boards_to_remove,
  796. )
  797. );
  798. // Delete this board's moderators.
  799. $smcFunc['db_query']('', '
  800. DELETE FROM {db_prefix}moderators
  801. WHERE id_board IN ({array_int:boards_to_remove})',
  802. array(
  803. 'boards_to_remove' => $boards_to_remove,
  804. )
  805. );
  806. // Delete any extra events in the calendar.
  807. $smcFunc['db_query']('', '
  808. DELETE FROM {db_prefix}calendar
  809. WHERE id_board IN ({array_int:boards_to_remove})',
  810. array(
  811. 'boards_to_remove' => $boards_to_remove,
  812. )
  813. );
  814. // Delete any message icons that only appear on these boards.
  815. $smcFunc['db_query']('', '
  816. DELETE FROM {db_prefix}message_icons
  817. WHERE id_board IN ({array_int:boards_to_remove})',
  818. array(
  819. 'boards_to_remove' => $boards_to_remove,
  820. )
  821. );
  822. // Delete the boards.
  823. $smcFunc['db_query']('', '
  824. DELETE FROM {db_prefix}boards
  825. WHERE id_board IN ({array_int:boards_to_remove})',
  826. array(
  827. 'boards_to_remove' => $boards_to_remove,
  828. )
  829. );
  830. // Latest message/topic might not be there anymore.
  831. updateStats('message');
  832. updateStats('topic');
  833. updateSettings(array(
  834. 'calendar_updated' => time(),
  835. ));
  836. // Plus reset the cache to stop people getting odd results.
  837. updateSettings(array('settings_updated' => time()));
  838. // Clean the cache as well.
  839. clean_cache('data');
  840. // Let's do some serious logging.
  841. foreach ($boards_to_remove as $id_board)
  842. logAction('delete_board', array('boardname' => $boards[$id_board]['name']), 'admin');
  843. reorderBoards();
  844. }
  845. // Put all boards in the right order.
  846. function reorderBoards()
  847. {
  848. global $cat_tree, $boardList, $boards, $smcFunc;
  849. getBoardTree();
  850. // Set the board order for each category.
  851. $board_order = 0;
  852. foreach ($cat_tree as $catID => $dummy)
  853. {
  854. foreach ($boardList[$catID] as $boardID)
  855. if ($boards[$boardID]['order'] != ++$board_order)
  856. $smcFunc['db_query']('', '
  857. UPDATE {db_prefix}boards
  858. SET board_order = {int:new_order}
  859. WHERE id_board = {int:selected_board}',
  860. array(
  861. 'new_order' => $board_order,
  862. 'selected_board' => $boardID,
  863. )
  864. );
  865. }
  866. // Sort the records of the boards table on the board_order value.
  867. $smcFunc['db_query']('alter_table_boards', '
  868. ALTER TABLE {db_prefix}boards
  869. ORDER BY board_order',
  870. array(
  871. 'db_error_skip' => true,
  872. )
  873. );
  874. }
  875. // Fixes the children of a board by setting their child_levels to new values.
  876. function fixChildren($parent, $newLevel, $newParent)
  877. {
  878. global $smcFunc;
  879. // Grab all children of $parent...
  880. $result = $smcFunc['db_query']('', '
  881. SELECT id_board
  882. FROM {db_prefix}boards
  883. WHERE id_parent = {int:parent_board}',
  884. array(
  885. 'parent_board' => $parent,
  886. )
  887. );
  888. $children = array();
  889. while ($row = $smcFunc['db_fetch_assoc']($result))
  890. $children[] = $row['id_board'];
  891. $smcFunc['db_free_result']($result);
  892. // ...and set it to a new parent and child_level.
  893. $smcFunc['db_query']('', '
  894. UPDATE {db_prefix}boards
  895. SET id_parent = {int:new_parent}, child_level = {int:new_child_level}
  896. WHERE id_parent = {int:parent_board}',
  897. array(
  898. 'new_parent' => $newParent,
  899. 'new_child_level' => $newLevel,
  900. 'parent_board' => $parent,
  901. )
  902. );
  903. // Recursively fix the children of the children.
  904. foreach ($children as $child)
  905. fixChildren($child, $newLevel + 1, $child);
  906. }
  907. // Load a lot of useful information regarding the boards and categories.
  908. function getBoardTree()
  909. {
  910. global $cat_tree, $boards, $boardList, $txt, $modSettings, $smcFunc;
  911. // Getting all the board and category information you'd ever wanted.
  912. $request = $smcFunc['db_query']('', '
  913. SELECT
  914. IFNULL(b.id_board, 0) AS id_board, b.id_parent, b.name AS board_name, b.description, b.child_level,
  915. b.board_order, b.count_posts, b.member_groups, b.id_theme, b.override_theme, b.id_profile, b.redirect,
  916. b.num_posts, b.num_topics, c.id_cat, c.name AS cat_name, c.cat_order, c.can_collapse
  917. FROM {db_prefix}categories AS c
  918. LEFT JOIN {db_prefix}boards AS b ON (b.id_cat = c.id_cat)
  919. ORDER BY c.cat_order, b.child_level, b.board_order',
  920. array(
  921. )
  922. );
  923. $cat_tree = array();
  924. $boards = array();
  925. $last_board_order = 0;
  926. while ($row = $smcFunc['db_fetch_assoc']($request))
  927. {
  928. if (!isset($cat_tree[$row['id_cat']]))
  929. {
  930. $cat_tree[$row['id_cat']] = array(
  931. 'node' => array(
  932. 'id' => $row['id_cat'],
  933. 'name' => $row['cat_name'],
  934. 'order' => $row['cat_order'],
  935. 'can_collapse' => $row['can_collapse']
  936. ),
  937. 'is_first' => empty($cat_tree),
  938. 'last_board_order' => $last_board_order,
  939. 'children' => array()
  940. );
  941. $prevBoard = 0;
  942. $curLevel = 0;
  943. }
  944. if (!empty($row['id_board']))
  945. {
  946. if ($row['child_level'] != $curLevel)
  947. $prevBoard = 0;
  948. $boards[$row['id_board']] = array(
  949. 'id' => $row['id_board'],
  950. 'category' => $row['id_cat'],
  951. 'parent' => $row['id_parent'],
  952. 'level' => $row['child_level'],
  953. 'order' => $row['board_order'],
  954. 'name' => $row['board_name'],
  955. 'member_groups' => explode(',', $row['member_groups']),
  956. 'description' => $row['description'],
  957. 'count_posts' => empty($row['count_posts']),
  958. 'posts' => $row['num_posts'],
  959. 'topics' => $row['num_topics'],
  960. 'theme' => $row['id_theme'],
  961. 'override_theme' => $row['override_theme'],
  962. 'profile' => $row['id_profile'],
  963. 'redirect' => $row['redirect'],
  964. 'prev_board' => $prevBoard
  965. );
  966. $prevBoard = $row['id_board'];
  967. $last_board_order = $row['board_order'];
  968. if (empty($row['child_level']))
  969. {
  970. $cat_tree[$row['id_cat']]['children'][$row['id_board']] = array(
  971. 'node' => &$boards[$row['id_board']],
  972. 'is_first' => empty($cat_tree[$row['id_cat']]['children']),
  973. 'children' => array()
  974. );
  975. $boards[$row['id_board']]['tree'] = &$cat_tree[$row['id_cat']]['children'][$row['id_board']];
  976. }
  977. else
  978. {
  979. // Parent doesn't exist!
  980. if (!isset($boards[$row['id_parent']]['tree']))
  981. fatal_lang_error('no_valid_parent', false, array($row['board_name']));
  982. // Wrong childlevel...we can silently fix this...
  983. if ($boards[$row['id_parent']]['tree']['node']['level'] != $row['child_level'] - 1)
  984. $smcFunc['db_query']('', '
  985. UPDATE {db_prefix}boards
  986. SET child_level = {int:new_child_level}
  987. WHERE id_board = {int:selected_board}',
  988. array(
  989. 'new_child_level' => $boards[$row['id_parent']]['tree']['node']['level'] + 1,
  990. 'selected_board' => $row['id_board'],
  991. )
  992. );
  993. $boards[$row['id_parent']]['tree']['children'][$row['id_board']] = array(
  994. 'node' => &$boards[$row['id_board']],
  995. 'is_first' => empty($boards[$row['id_parent']]['tree']['children']),
  996. 'children' => array()
  997. );
  998. $boards[$row['id_board']]['tree'] = &$boards[$row['id_parent']]['tree']['children'][$row['id_board']];
  999. }
  1000. }
  1001. }
  1002. $smcFunc['db_free_result']($request);
  1003. // Get a list of all the boards in each category (using recursion).
  1004. $boardList = array();
  1005. foreach ($cat_tree as $catID => $node)
  1006. {
  1007. $boardList[$catID] = array();
  1008. recursiveBoards($boardList[$catID], $node);
  1009. }
  1010. }
  1011. // Recursively get a list of boards.
  1012. function recursiveBoards(&$_boardList, &$_tree)
  1013. {
  1014. if (empty($_tree['children']))
  1015. return;
  1016. foreach ($_tree['children'] as $id => $node)
  1017. {
  1018. $_boardList[] = $id;
  1019. recursiveBoards($_boardList, $node);
  1020. }
  1021. }
  1022. // Returns whether the child board id is actually a child of the parent (recursive).
  1023. function isChildOf($child, $parent)
  1024. {
  1025. global $boards;
  1026. if (empty($boards[$child]['parent']))
  1027. return false;
  1028. if ($boards[$child]['parent'] == $parent)
  1029. return true;
  1030. return isChildOf($boards[$child]['parent'], $parent);
  1031. }
  1032. ?>