BioTorrents.de’s version of Gazelle
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

comments.class.php 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. <?
  2. class Comments {
  3. /*
  4. * For all functions:
  5. * $Page = 'artist', 'collages', 'requests' or 'torrents'
  6. * $PageID = ArtistID, CollageID, RequestID or GroupID, respectively
  7. */
  8. /**
  9. * Post a comment on an artist, request or torrent page.
  10. * @param string $Page
  11. * @param int $PageID
  12. * @param string $Body
  13. * @return int ID of the new comment
  14. */
  15. public static function post($Page, $PageID, $Body) {
  16. $QueryID = G::$DB->get_query_id();
  17. G::$DB->query("
  18. SELECT
  19. CEIL(
  20. (
  21. SELECT COUNT(ID) + 1
  22. FROM comments
  23. WHERE Page = '$Page'
  24. AND PageID = $PageID
  25. ) / " . TORRENT_COMMENTS_PER_PAGE . "
  26. ) AS Pages");
  27. list($Pages) = G::$DB->next_record();
  28. G::$DB->query("
  29. INSERT INTO comments (Page, PageID, AuthorID, AddedTime, Body)
  30. VALUES ('$Page', $PageID, " . G::$LoggedUser['ID'] . ", NOW(), '" . db_string($Body) . "')");
  31. $PostID = G::$DB->inserted_id();
  32. $CatalogueID = floor((TORRENT_COMMENTS_PER_PAGE * $Pages - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE);
  33. G::$Cache->delete_value($Page.'_comments_'.$PageID.'_catalogue_'.$CatalogueID);
  34. G::$Cache->delete_value($Page.'_comments_'.$PageID);
  35. Subscriptions::flush_subscriptions($Page, $PageID);
  36. Subscriptions::quote_notify($Body, $PostID, $Page, $PageID);
  37. G::$DB->set_query_id($QueryID);
  38. return $PostID;
  39. }
  40. /**
  41. * Edit a comment
  42. * @param int $PostID
  43. * @param string $NewBody
  44. * @param bool $SendPM If true, send a PM to the author of the comment informing him about the edit
  45. *
  46. * todo: Move permission check out of here/remove hardcoded error(404)
  47. */
  48. public static function edit($PostID, $NewBody, $SendPM = false) {
  49. $QueryID = G::$DB->get_query_id();
  50. G::$DB->query("
  51. SELECT
  52. Body,
  53. AuthorID,
  54. Page,
  55. PageID,
  56. AddedTime
  57. FROM comments
  58. WHERE ID = $PostID");
  59. if (!G::$DB->has_results()) {
  60. return false;
  61. }
  62. list($OldBody, $AuthorID, $Page, $PageID, $AddedTime) = G::$DB->next_record();
  63. if (G::$LoggedUser['ID'] != $AuthorID && !check_perms('site_moderate_forums')) {
  64. return false;
  65. }
  66. G::$DB->query("
  67. SELECT CEIL(COUNT(ID) / " . TORRENT_COMMENTS_PER_PAGE . ") AS Page
  68. FROM comments
  69. WHERE Page = '$Page'
  70. AND PageID = $PageID
  71. AND ID <= $PostID");
  72. list($CommPage) = G::$DB->next_record();
  73. // Perform the update
  74. G::$DB->query("
  75. UPDATE comments
  76. SET
  77. Body = '" . db_string($NewBody) . "',
  78. EditedUserID = " . G::$LoggedUser['ID'] . ",
  79. EditedTime = NOW()
  80. WHERE ID = $PostID");
  81. // Update the cache
  82. $CatalogueID = floor((TORRENT_COMMENTS_PER_PAGE * $CommPage - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE);
  83. G::$Cache->delete_value($Page . '_comments_' . $PageID . '_catalogue_' . $CatalogueID);
  84. if ($Page == 'collages') {
  85. // On collages, we also need to clear the collage key (collage_$CollageID), because it has the comments in it... (why??)
  86. G::$Cache->delete_value('collage_' . $PageID);
  87. }
  88. G::$DB->query("
  89. INSERT INTO comments_edits (Page, PostID, EditUser, EditTime, Body)
  90. VALUES ('$Page', $PostID, " . G::$LoggedUser['ID'] . ", NOW(), '" . db_string($OldBody) . "')");
  91. G::$DB->set_query_id($QueryID);
  92. if ($SendPM && G::$LoggedUser['ID'] != $AuthorID) {
  93. // Send a PM to the user to notify them of the edit
  94. $PMSubject = "Your comment #$PostID has been edited";
  95. $PMurl = site_url()."comments.php?action=jump&postid=$PostID";
  96. $ProfLink = '[url='.site_url().'user.php?id='.G::$LoggedUser['ID'].']'.G::$LoggedUser['Username'].'[/url]';
  97. $PMBody = "One of your comments has been edited by $ProfLink: [url]{$PMurl}[/url]";
  98. Misc::send_pm($AuthorID, 0, $PMSubject, $PMBody);
  99. }
  100. return true; // todo: This should reflect whether or not the update was actually successful, e.g., by checking G::$DB->affected_rows after the UPDATE query
  101. }
  102. /**
  103. * Delete a comment
  104. * @param int $PostID
  105. */
  106. public static function delete($PostID) {
  107. $QueryID = G::$DB->get_query_id();
  108. // Get page, pageid
  109. G::$DB->query("SELECT Page, PageID FROM comments WHERE ID = $PostID");
  110. if (!G::$DB->has_results()) {
  111. // no such comment?
  112. G::$DB->set_query_id($QueryID);
  113. return false;
  114. }
  115. list ($Page, $PageID) = G::$DB->next_record();
  116. // get number of pages
  117. G::$DB->query("
  118. SELECT
  119. CEIL(COUNT(ID) / " . TORRENT_COMMENTS_PER_PAGE . ") AS Pages,
  120. CEIL(SUM(IF(ID <= $PostID, 1, 0)) / " . TORRENT_COMMENTS_PER_PAGE . ") AS Page
  121. FROM comments
  122. WHERE Page = '$Page'
  123. AND PageID = $PageID
  124. GROUP BY PageID");
  125. if (!G::$DB->has_results()) {
  126. // the comment $PostID was probably not posted on $Page
  127. G::$DB->set_query_id($QueryID);
  128. return false;
  129. }
  130. list($CommPages, $CommPage) = G::$DB->next_record();
  131. // $CommPages = number of pages in the thread
  132. // $CommPage = which page the post is on
  133. // These are set for cache clearing.
  134. G::$DB->query("
  135. DELETE FROM comments
  136. WHERE ID = $PostID");
  137. G::$DB->query("
  138. DELETE FROM comments_edits
  139. WHERE Page = '$Page'
  140. AND PostID = $PostID");
  141. G::$DB->query("
  142. DELETE FROM users_notify_quoted
  143. WHERE Page = '$Page'
  144. AND PostID = $PostID");
  145. Subscriptions::flush_subscriptions($Page, $PageID);
  146. Subscriptions::flush_quote_notifications($Page, $PageID);
  147. //We need to clear all subsequential catalogues as they've all been bumped with the absence of this post
  148. $ThisCatalogue = floor((TORRENT_COMMENTS_PER_PAGE * $CommPage - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE);
  149. $LastCatalogue = floor((TORRENT_COMMENTS_PER_PAGE * $CommPages - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE);
  150. for ($i = $ThisCatalogue; $i <= $LastCatalogue; ++$i) {
  151. G::$Cache->delete_value($Page . '_comments_' . $PageID . '_catalogue_' . $i);
  152. }
  153. G::$Cache->delete_value($Page . '_comments_' . $PageID);
  154. if ($Page == 'collages') {
  155. // On collages, we also need to clear the collage key (collage_$CollageID), because it has the comments in it... (why??)
  156. G::$Cache->delete_value("collage_$PageID");
  157. }
  158. G::$DB->set_query_id($QueryID);
  159. return true;
  160. }
  161. /**
  162. * Get the URL to a comment, already knowing the Page and PostID
  163. * @param string $Page
  164. * @param int $PageID
  165. * @param int $PostID
  166. * @return string|bool The URL to the comment or false on error
  167. */
  168. public static function get_url($Page, $PageID, $PostID = null) {
  169. $Post = (!empty($PostID) ? "&postid=$PostID#post$PostID" : '');
  170. switch ($Page) {
  171. case 'artist':
  172. return "artist.php?id=$PageID$Post";
  173. case 'collages':
  174. return "collages.php?action=comments&collageid=$PageID$Post";
  175. case 'requests':
  176. return "requests.php?action=view&id=$PageID$Post";
  177. case 'torrents':
  178. return "torrents.php?id=$PageID$Post";
  179. default:
  180. return false;
  181. }
  182. }
  183. /**
  184. * Get the URL to a comment
  185. * @param int $PostID
  186. * @return string|bool The URL to the comment or false on error
  187. */
  188. public static function get_url_query($PostID) {
  189. $QueryID = G::$DB->get_query_id();
  190. G::$DB->query("
  191. SELECT Page, PageID
  192. FROM comments
  193. WHERE ID = $PostID");
  194. if (!G::$DB->has_results()) {
  195. error(404);
  196. }
  197. list($Page, $PageID) = G::$DB->next_record();
  198. G::$DB->set_query_id($QueryID);
  199. return self::get_url($Page, $PageID, $PostID);
  200. }
  201. /**
  202. * Load a page's comments. This takes care of `postid` and (indirectly) `page` parameters passed in $_GET.
  203. * Quote notifications and last read are also handled here, unless $HandleSubscriptions = false is passed.
  204. * @param string $Page
  205. * @param int $PageID
  206. * @param bool $HandleSubscriptions Whether or not to handle subscriptions (last read & quote notifications)
  207. * @return array ($NumComments, $Page, $Thread, $LastRead)
  208. * $NumComments: the total number of comments on this artist/request/torrent group
  209. * $Page: the page we're currently on
  210. * $Thread: an array of all posts on this page
  211. * $LastRead: ID of the last comment read by the current user in this thread;
  212. * will be false if $HandleSubscriptions == false or if there are no comments on this page
  213. */
  214. public static function load($Page, $PageID, $HandleSubscriptions = true) {
  215. $QueryID = G::$DB->get_query_id();
  216. // Get the total number of comments
  217. $NumComments = G::$Cache->get_value($Page."_comments_$PageID");
  218. if ($NumComments === false) {
  219. G::$DB->query("
  220. SELECT COUNT(ID)
  221. FROM comments
  222. WHERE Page = '$Page'
  223. AND PageID = $PageID");
  224. list($NumComments) = G::$DB->next_record();
  225. G::$Cache->cache_value($Page."_comments_$PageID", $NumComments, 0);
  226. }
  227. // If a postid was passed, we need to determine which page that comment is on.
  228. // Format::page_limit handles a potential $_GET['page']
  229. if (isset($_GET['postid']) && is_number($_GET['postid']) && $NumComments > TORRENT_COMMENTS_PER_PAGE) {
  230. G::$DB->query("
  231. SELECT COUNT(ID)
  232. FROM comments
  233. WHERE Page = '$Page'
  234. AND PageID = $PageID
  235. AND ID <= $_GET[postid]");
  236. list($PostNum) = G::$DB->next_record();
  237. list($CommPage, $Limit) = Format::page_limit(TORRENT_COMMENTS_PER_PAGE, $PostNum);
  238. } else {
  239. list($CommPage, $Limit) = Format::page_limit(TORRENT_COMMENTS_PER_PAGE, $NumComments);
  240. }
  241. // Get the cache catalogue
  242. $CatalogueID = floor((TORRENT_COMMENTS_PER_PAGE * $CommPage - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE);
  243. // Cache catalogue from which the page is selected, allows block caches and future ability to specify posts per page
  244. $Catalogue = G::$Cache->get_value($Page.'_comments_'.$PageID.'_catalogue_'.$CatalogueID);
  245. if ($Catalogue === false) {
  246. $CatalogueLimit = $CatalogueID * THREAD_CATALOGUE . ', ' . THREAD_CATALOGUE;
  247. G::$DB->query("
  248. SELECT
  249. c.ID,
  250. c.AuthorID,
  251. c.AddedTime,
  252. c.Body,
  253. c.EditedUserID,
  254. c.EditedTime,
  255. u.Username
  256. FROM comments AS c
  257. LEFT JOIN users_main AS u ON u.ID = c.EditedUserID
  258. WHERE c.Page = '$Page'
  259. AND c.PageID = $PageID
  260. ORDER BY c.ID
  261. LIMIT $CatalogueLimit");
  262. $Catalogue = G::$DB->to_array(false, MYSQLI_ASSOC);
  263. G::$Cache->cache_value($Page.'_comments_'.$PageID.'_catalogue_'.$CatalogueID, $Catalogue, 0);
  264. }
  265. //This is a hybrid to reduce the catalogue down to the page elements: We use the page limit % catalogue
  266. $Thread = array_slice($Catalogue, ((TORRENT_COMMENTS_PER_PAGE * $CommPage - TORRENT_COMMENTS_PER_PAGE) % THREAD_CATALOGUE), TORRENT_COMMENTS_PER_PAGE, true);
  267. if ($HandleSubscriptions && count($Thread) > 0) {
  268. // quote notifications
  269. $LastPost = end($Thread);
  270. $LastPost = $LastPost['ID'];
  271. $FirstPost = reset($Thread);
  272. $FirstPost = $FirstPost['ID'];
  273. G::$DB->query("
  274. UPDATE users_notify_quoted
  275. SET UnRead = false
  276. WHERE UserID = " . G::$LoggedUser['ID'] . "
  277. AND Page = '$Page'
  278. AND PageID = $PageID
  279. AND PostID >= $FirstPost
  280. AND PostID <= $LastPost");
  281. if (G::$DB->affected_rows()) {
  282. G::$Cache->delete_value('notify_quoted_' . G::$LoggedUser['ID']);
  283. }
  284. // last read
  285. G::$DB->query("
  286. SELECT PostID
  287. FROM users_comments_last_read
  288. WHERE UserID = " . G::$LoggedUser['ID'] . "
  289. AND Page = '$Page'
  290. AND PageID = $PageID");
  291. list($LastRead) = G::$DB->next_record();
  292. if ($LastRead < $LastPost) {
  293. G::$DB->query("
  294. INSERT INTO users_comments_last_read
  295. (UserID, Page, PageID, PostID)
  296. VALUES
  297. (" . G::$LoggedUser['ID'] . ", '$Page', $PageID, $LastPost)
  298. ON DUPLICATE KEY UPDATE
  299. PostID = $LastPost");
  300. G::$Cache->delete_value('subscriptions_user_new_' . G::$LoggedUser['ID']);
  301. }
  302. } else {
  303. $LastRead = false;
  304. }
  305. G::$DB->set_query_id($QueryID);
  306. return array($NumComments, $CommPage, $Thread, $LastRead);
  307. }
  308. /**
  309. * Merges all comments from $Page/$PageID into $Page/$TargetPageID. This also takes care of quote notifications, subscriptions and cache.
  310. * @param type $Page
  311. * @param type $PageID
  312. * @param type $TargetPageID
  313. */
  314. public static function merge($Page, $PageID, $TargetPageID) {
  315. $QueryID = G::$DB->get_query_id();
  316. G::$DB->query("
  317. UPDATE comments
  318. SET PageID = $TargetPageID
  319. WHERE Page = '$Page'
  320. AND PageID = $PageID");
  321. // quote notifications
  322. G::$DB->query("
  323. UPDATE users_notify_quoted
  324. SET PageID = $TargetPageID
  325. WHERE Page = '$Page'
  326. AND PageID = $PageID");
  327. // comment subscriptions
  328. Subscriptions::move_subscriptions($Page, $PageID, $TargetPageID);
  329. // cache (we need to clear all comment catalogues)
  330. G::$DB->query("
  331. SELECT
  332. CEIL(COUNT(ID) / " . TORRENT_COMMENTS_PER_PAGE . ") AS Pages
  333. FROM comments
  334. WHERE Page = '$Page'
  335. AND PageID = $TargetPageID
  336. GROUP BY PageID");
  337. list($CommPages) = G::$DB->next_record();
  338. $LastCatalogue = floor((TORRENT_COMMENTS_PER_PAGE * $CommPages - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE);
  339. for ($i = 0; $i <= $LastCatalogue; ++$i) {
  340. G::$Cache->delete_value($Page . "_comments_$TargetPageID" . "_catalogue_$i");
  341. }
  342. G::$Cache->delete_value($Page."_comments_$TargetPageID");
  343. G::$DB->set_query_id($QueryID);
  344. }
  345. /**
  346. * Delete all comments on $Page/$PageID (deals with quote notifications and subscriptions as well)
  347. * @param string $Page
  348. * @param int $PageID
  349. * @return boolean
  350. */
  351. public static function delete_page($Page, $PageID) {
  352. $QueryID = G::$DB->get_query_id();
  353. // get number of pages
  354. G::$DB->query("
  355. SELECT
  356. CEIL(COUNT(ID) / " . TORRENT_COMMENTS_PER_PAGE . ") AS Pages
  357. FROM comments
  358. WHERE Page = '$Page'
  359. AND PageID = $PageID
  360. GROUP BY PageID");
  361. if (!G::$DB->has_results()) {
  362. return false;
  363. }
  364. list($CommPages) = G::$DB->next_record();
  365. // Delete comments
  366. G::$DB->query("
  367. DELETE FROM comments
  368. WHERE Page = '$Page'
  369. AND PageID = $PageID");
  370. // Delete quote notifications
  371. Subscriptions::flush_quote_notifications($Page, $PageID);
  372. G::$DB->query("
  373. DELETE FROM users_notify_quoted
  374. WHERE Page = '$Page'
  375. AND PageID = $PageID");
  376. // Deal with subscriptions
  377. Subscriptions::move_subscriptions($Page, $PageID, null);
  378. // Clear cache
  379. $LastCatalogue = floor((TORRENT_COMMENTS_PER_PAGE * $CommPages - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE);
  380. for ($i = 0; $i <= $LastCatalogue; ++$i) {
  381. G::$Cache->delete_value($Page . '_comments_' . $PageID . '_catalogue_' . $i);
  382. }
  383. G::$Cache->delete_value($Page.'_comments_'.$PageID);
  384. G::$DB->set_query_id($QueryID);
  385. return true;
  386. }
  387. }