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.

subscriptions.class.php 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. <?php
  2. class Subscriptions
  3. {
  4. /**
  5. * Parse a post/comment body for quotes and notify all quoted users that have quote notifications enabled.
  6. * @param string $Body
  7. * @param int $PostID
  8. * @param string $Page
  9. * @param int $PageID
  10. */
  11. public static function quote_notify($Body, $PostID, $Page, $PageID)
  12. {
  13. $QueryID = G::$DB->get_query_id();
  14. /*
  15. * Explanation of the parameters PageID and Page: Page contains where
  16. * this quote comes from and can be forums, artist, collages, requests
  17. * or torrents. The PageID contains the additional value that is
  18. * necessary for the users_notify_quoted table. The PageIDs for the
  19. * different Page are: forums: TopicID artist: ArtistID collages:
  20. * CollageID requests: RequestID torrents: GroupID
  21. */
  22. $Matches = [];
  23. preg_match_all('/\[quote(?:=(.*)(?:\|.*)?)?]|\[\/quote]/iU', $Body, $Matches, PREG_SET_ORDER);
  24. if (count($Matches)) {
  25. $Usernames = [];
  26. $Level = 0;
  27. foreach ($Matches as $M) {
  28. if ($M[0] != '[/quote]') {
  29. if ($Level == 0 && isset($M[1]) && strlen($M[1]) > 0 && preg_match(USERNAME_REGEX, $M[1])) {
  30. $Usernames[] = preg_replace('/(^[.,]*)|([.,]*$)/', '', $M[1]); // wut?
  31. }
  32. ++$Level;
  33. } else {
  34. --$Level;
  35. }
  36. }
  37. }
  38. // remove any dupes in the array (the fast way)
  39. $Usernames = array_flip(array_flip($Usernames));
  40. G::$DB->query("
  41. SELECT m.ID
  42. FROM users_main AS m
  43. LEFT JOIN users_info AS i ON i.UserID = m.ID
  44. WHERE m.Username IN ('" . implode("', '", $Usernames) . "')
  45. AND i.NotifyOnQuote = '1'
  46. AND i.UserID != " . G::$LoggedUser['ID']);
  47. $Results = G::$DB->to_array();
  48. foreach ($Results as $Result) {
  49. $UserID = db_string($Result['ID']);
  50. $QuoterID = db_string(G::$LoggedUser['ID']);
  51. $Page = db_string($Page);
  52. $PageID = db_string($PageID);
  53. $PostID = db_string($PostID);
  54. G::$DB->query(
  55. "
  56. INSERT IGNORE INTO users_notify_quoted
  57. (UserID, QuoterID, Page, PageID, PostID, Date)
  58. VALUES
  59. ( ?, ?, ?, ?, ?, NOW())",
  60. $Result['ID'],
  61. G::$LoggedUser['ID'],
  62. $Page,
  63. $PageID,
  64. $PostID
  65. );
  66. G::$Cache->delete_value("notify_quoted_$UserID");
  67. if ($Page == 'forums') {
  68. $URL = site_url() . "forums.php?action=viewthread&postid=$PostID";
  69. } else {
  70. $URL = site_url() . "comments.php?action=jump&postid=$PostID";
  71. }
  72. }
  73. G::$DB->set_query_id($QueryID);
  74. }
  75. /**
  76. * (Un)subscribe from a forum thread.
  77. * If UserID == 0, G::$LoggedUser[ID] is used
  78. * @param int $TopicID
  79. * @param int $UserID
  80. */
  81. public static function subscribe($TopicID, $UserID = 0)
  82. {
  83. if ($UserID == 0) {
  84. $UserID = G::$LoggedUser['ID'];
  85. }
  86. $QueryID = G::$DB->get_query_id();
  87. $UserSubscriptions = self::get_subscriptions();
  88. $Key = self::has_subscribed($TopicID);
  89. if ($Key !== false) {
  90. G::$DB->query('
  91. DELETE FROM users_subscriptions
  92. WHERE UserID = ' . db_string($UserID) . '
  93. AND TopicID = ' . db_string($TopicID));
  94. unset($UserSubscriptions[$Key]);
  95. } else {
  96. G::$DB->query("
  97. INSERT IGNORE INTO users_subscriptions (UserID, TopicID)
  98. VALUES ($UserID, " . db_string($TopicID) . ")");
  99. array_push($UserSubscriptions, $TopicID);
  100. }
  101. G::$Cache->replace_value("subscriptions_user_$UserID", $UserSubscriptions, 0);
  102. G::$Cache->delete_value("subscriptions_user_new_$UserID");
  103. G::$DB->set_query_id($QueryID);
  104. }
  105. /**
  106. * (Un)subscribe from comments.
  107. * If UserID == 0, G::$LoggedUser[ID] is used
  108. * @param string $Page 'artist', 'collages', 'requests' or 'torrents'
  109. * @param int $PageID ArtistID, CollageID, RequestID or GroupID
  110. * @param int $UserID
  111. */
  112. public static function subscribe_comments($Page, $PageID, $UserID = 0)
  113. {
  114. if ($UserID == 0) {
  115. $UserID = G::$LoggedUser['ID'];
  116. }
  117. $QueryID = G::$DB->get_query_id();
  118. $UserCommentSubscriptions = self::get_comment_subscriptions();
  119. $Key = self::has_subscribed_comments($Page, $PageID);
  120. if ($Key !== false) {
  121. G::$DB->query("
  122. DELETE FROM users_subscriptions_comments
  123. WHERE UserID = " . db_string($UserID) . "
  124. AND Page = '" . db_string($Page) . "'
  125. AND PageID = " . db_string($PageID));
  126. unset($UserCommentSubscriptions[$Key]);
  127. } else {
  128. G::$DB->query("
  129. INSERT IGNORE INTO users_subscriptions_comments
  130. (UserID, Page, PageID)
  131. VALUES
  132. ($UserID, '" . db_string($Page) . "', " . db_string($PageID) . ")");
  133. array_push($UserCommentSubscriptions, array($Page, $PageID));
  134. }
  135. G::$Cache->replace_value("subscriptions_comments_user_$UserID", $UserCommentSubscriptions, 0);
  136. G::$Cache->delete_value("subscriptions_comments_user_new_$UserID");
  137. G::$DB->set_query_id($QueryID);
  138. }
  139. /**
  140. * Read $UserID's subscriptions. If the cache key isn't set, it gets filled.
  141. * If UserID == 0, G::$LoggedUser[ID] is used
  142. * @param int $UserID
  143. * @return array Array of TopicIDs
  144. */
  145. public static function get_subscriptions($UserID = 0)
  146. {
  147. if ($UserID == 0) {
  148. $UserID = G::$LoggedUser['ID'];
  149. }
  150. $QueryID = G::$DB->get_query_id();
  151. $UserSubscriptions = G::$Cache->get_value("subscriptions_user_$UserID");
  152. if ($UserSubscriptions === false) {
  153. G::$DB->query('
  154. SELECT TopicID
  155. FROM users_subscriptions
  156. WHERE UserID = ' . db_string($UserID));
  157. $UserSubscriptions = G::$DB->collect(0);
  158. G::$Cache->cache_value("subscriptions_user_$UserID", $UserSubscriptions, 0);
  159. }
  160. G::$DB->set_query_id($QueryID);
  161. return $UserSubscriptions;
  162. }
  163. /**
  164. * Same as self::get_subscriptions, but for comment subscriptions
  165. * @param int $UserID
  166. * @return array Array of ($Page, $PageID)
  167. */
  168. public static function get_comment_subscriptions($UserID = 0)
  169. {
  170. if ($UserID == 0) {
  171. $UserID = G::$LoggedUser['ID'];
  172. }
  173. $QueryID = G::$DB->get_query_id();
  174. $UserCommentSubscriptions = G::$Cache->get_value("subscriptions_comments_user_$UserID");
  175. if ($UserCommentSubscriptions === false) {
  176. G::$DB->query('
  177. SELECT Page, PageID
  178. FROM users_subscriptions_comments
  179. WHERE UserID = ' . db_string($UserID));
  180. $UserCommentSubscriptions = G::$DB->to_array(false, MYSQLI_NUM);
  181. G::$Cache->cache_value("subscriptions_comments_user_$UserID", $UserCommentSubscriptions, 0);
  182. }
  183. G::$DB->set_query_id($QueryID);
  184. return $UserCommentSubscriptions;
  185. }
  186. /**
  187. * Returns whether or not the current user has new subscriptions. This handles both forum and comment subscriptions.
  188. * @return int Number of unread subscribed threads/comments
  189. */
  190. public static function has_new_subscriptions()
  191. {
  192. $QueryID = G::$DB->get_query_id();
  193. $NewSubscriptions = G::$Cache->get_value('subscriptions_user_new_' . G::$LoggedUser['ID']);
  194. if ($NewSubscriptions === false) {
  195. // forum subscriptions
  196. G::$DB->query("
  197. SELECT COUNT(1)
  198. FROM users_subscriptions AS s
  199. LEFT JOIN forums_last_read_topics AS l ON l.UserID = s.UserID AND l.TopicID = s.TopicID
  200. JOIN forums_topics AS t ON t.ID = s.TopicID
  201. JOIN forums AS f ON f.ID = t.ForumID
  202. WHERE " . Forums::user_forums_sql() . "
  203. AND IF(t.IsLocked = '1' AND t.IsSticky = '0'" . ", t.LastPostID, IF(l.PostID IS NULL, 0, l.PostID)) < t.LastPostID
  204. AND s.UserID = " . G::$LoggedUser['ID']);
  205. list($NewForumSubscriptions) = G::$DB->next_record();
  206. // comment subscriptions
  207. G::$DB->query("
  208. SELECT COUNT(1)
  209. FROM users_subscriptions_comments AS s
  210. LEFT JOIN users_comments_last_read AS lr ON lr.UserID = s.UserID AND lr.Page = s.Page AND lr.PageID = s.PageID
  211. LEFT JOIN comments AS c ON c.ID = (SELECT MAX(ID) FROM comments WHERE Page = s.Page AND PageID = s.PageID)
  212. LEFT JOIN collages AS co ON s.Page = 'collages' AND co.ID = s.PageID
  213. WHERE s.UserID = " . G::$LoggedUser['ID'] . "
  214. AND (s.Page != 'collages' OR co.Deleted = '0')
  215. AND IF(lr.PostID IS NULL, 0, lr.PostID) < c.ID");
  216. list($NewCommentSubscriptions) = G::$DB->next_record();
  217. $NewSubscriptions = $NewForumSubscriptions + $NewCommentSubscriptions;
  218. G::$Cache->cache_value('subscriptions_user_new_' . G::$LoggedUser['ID'], $NewSubscriptions, 0);
  219. }
  220. G::$DB->set_query_id($QueryID);
  221. return (int)$NewSubscriptions;
  222. }
  223. /**
  224. * Returns whether or not the current user has new quote notifications.
  225. * @return int Number of unread quote notifications
  226. */
  227. public static function has_new_quote_notifications()
  228. {
  229. $QuoteNotificationsCount = G::$Cache->get_value('notify_quoted_' . G::$LoggedUser['ID']);
  230. if ($QuoteNotificationsCount === false) {
  231. $sql = "
  232. SELECT COUNT(1)
  233. FROM users_notify_quoted AS q
  234. LEFT JOIN forums_topics AS t ON t.ID = q.PageID
  235. LEFT JOIN forums AS f ON f.ID = t.ForumID
  236. LEFT JOIN collages AS c ON q.Page = 'collages' AND c.ID = q.PageID
  237. WHERE q.UserID = " . G::$LoggedUser['ID'] . "
  238. AND q.UnRead
  239. AND (q.Page != 'forums' OR " . Forums::user_forums_sql() . ")
  240. AND (q.Page != 'collages' OR c.Deleted = '0')";
  241. $QueryID = G::$DB->get_query_id();
  242. G::$DB->query($sql);
  243. list($QuoteNotificationsCount) = G::$DB->next_record();
  244. G::$DB->set_query_id($QueryID);
  245. G::$Cache->cache_value('notify_quoted_' . G::$LoggedUser['ID'], $QuoteNotificationsCount, 0);
  246. }
  247. return (int)$QuoteNotificationsCount;
  248. }
  249. /**
  250. * Returns the key which holds this $TopicID in the subscription array.
  251. * Use type-aware comparison operators with this! (ie. if (self::has_subscribed($TopicID) !== false) { ... })
  252. * @param int $TopicID
  253. * @return bool|int
  254. */
  255. public static function has_subscribed($TopicID)
  256. {
  257. $UserSubscriptions = self::get_subscriptions();
  258. return array_search($TopicID, $UserSubscriptions);
  259. }
  260. /**
  261. * Same as has_subscribed, but for comment subscriptions.
  262. * @param string $Page 'artist', 'collages', 'requests' or 'torrents'
  263. * @param int $PageID
  264. * @return bool|int
  265. */
  266. public static function has_subscribed_comments($Page, $PageID)
  267. {
  268. $UserCommentSubscriptions = self::get_comment_subscriptions();
  269. return array_search(array($Page, $PageID), $UserCommentSubscriptions);
  270. }
  271. /**
  272. * Clear the subscription cache for all subscribers of a forum thread or artist/collage/request/torrent comments.
  273. * @param type $Page 'forums', 'artist', 'collages', 'requests' or 'torrents'
  274. * @param type $PageID TopicID, ArtistID, CollageID, RequestID or GroupID, respectively
  275. */
  276. public static function flush_subscriptions($Page, $PageID)
  277. {
  278. $QueryID = G::$DB->get_query_id();
  279. if ($Page == 'forums') {
  280. G::$DB->query("
  281. SELECT UserID
  282. FROM users_subscriptions
  283. WHERE TopicID = '$PageID'");
  284. } else {
  285. G::$DB->query("
  286. SELECT UserID
  287. FROM users_subscriptions_comments
  288. WHERE Page = '$Page'
  289. AND PageID = '$PageID'");
  290. }
  291. $Subscribers = G::$DB->collect('UserID');
  292. foreach ($Subscribers as $Subscriber) {
  293. G::$Cache->delete_value("subscriptions_user_new_$Subscriber");
  294. }
  295. G::$DB->set_query_id($QueryID);
  296. }
  297. /**
  298. * Move all $Page subscriptions from $OldPageID to $NewPageID (for example when merging torrent groups).
  299. * Passing $NewPageID = null will delete the subscriptions.
  300. * @param string $Page 'forums', 'artist', 'collages', 'requests' or 'torrents'
  301. * @param int $OldPageID TopicID, ArtistID, CollageID, RequestID or GroupID, respectively
  302. * @param int|null $NewPageID As $OldPageID, or null to delete the subscriptions
  303. */
  304. public static function move_subscriptions($Page, $OldPageID, $NewPageID)
  305. {
  306. self::flush_subscriptions($Page, $OldPageID);
  307. $QueryID = G::$DB->get_query_id();
  308. if ($Page == 'forums') {
  309. if ($NewPageID !== null) {
  310. G::$DB->query("
  311. UPDATE IGNORE users_subscriptions
  312. SET TopicID = '$NewPageID'
  313. WHERE TopicID = '$OldPageID'");
  314. // explanation see below
  315. G::$DB->query("
  316. UPDATE IGNORE forums_last_read_topics
  317. SET TopicID = $NewPageID
  318. WHERE TopicID = $OldPageID");
  319. G::$DB->query("
  320. SELECT UserID, MIN(PostID)
  321. FROM forums_last_read_topics
  322. WHERE TopicID IN ($OldPageID, $NewPageID)
  323. GROUP BY UserID
  324. HAVING COUNT(1) = 2");
  325. $Results = G::$DB->to_array(false, MYSQLI_NUM);
  326. foreach ($Results as $Result) {
  327. G::$DB->query("
  328. UPDATE forums_last_read_topics
  329. SET PostID = $Result[1]
  330. WHERE TopicID = $NewPageID
  331. AND UserID = $Result[0]");
  332. }
  333. }
  334. G::$DB->query("
  335. DELETE FROM users_subscriptions
  336. WHERE TopicID = '$OldPageID'");
  337. G::$DB->query("
  338. DELETE FROM forums_last_read_topics
  339. WHERE TopicID = $OldPageID");
  340. } else {
  341. if ($NewPageID !== null) {
  342. G::$DB->query("
  343. UPDATE IGNORE users_subscriptions_comments
  344. SET PageID = '$NewPageID'
  345. WHERE Page = '$Page'
  346. AND PageID = '$OldPageID'");
  347. // last read handling
  348. // 1) update all rows that have no key collisions (i.e. users that haven't previously read both pages or if there are only comments on one page)
  349. G::$DB->query("
  350. UPDATE IGNORE users_comments_last_read
  351. SET PageID = '$NewPageID'
  352. WHERE Page = '$Page'
  353. AND PageID = $OldPageID");
  354. // 2) get all last read records with key collisions (i.e. there are records for one user for both PageIDs)
  355. G::$DB->query("
  356. SELECT UserID, MIN(PostID)
  357. FROM users_comments_last_read
  358. WHERE Page = '$Page'
  359. AND PageID IN ($OldPageID, $NewPageID)
  360. GROUP BY UserID
  361. HAVING COUNT(1) = 2");
  362. $Results = G::$DB->to_array(false, MYSQLI_NUM);
  363. // 3) update rows for those people found in 2) to the earlier post
  364. foreach ($Results as $Result) {
  365. G::$DB->query("
  366. UPDATE users_comments_last_read
  367. SET PostID = $Result[1]
  368. WHERE Page = '$Page'
  369. AND PageID = $NewPageID
  370. AND UserID = $Result[0]");
  371. }
  372. }
  373. G::$DB->query("
  374. DELETE FROM users_subscriptions_comments
  375. WHERE Page = '$Page'
  376. AND PageID = '$OldPageID'");
  377. G::$DB->query("
  378. DELETE FROM users_comments_last_read
  379. WHERE Page = '$Page'
  380. AND PageID = '$OldPageID'");
  381. }
  382. G::$DB->set_query_id($QueryID);
  383. }
  384. /**
  385. * Clear the quote notification cache for all subscribers of a forum thread or artist/collage/request/torrent comments.
  386. * @param string $Page 'forums', 'artist', 'collages', 'requests' or 'torrents'
  387. * @param int $PageID TopicID, ArtistID, CollageID, RequestID or GroupID, respectively
  388. */
  389. public static function flush_quote_notifications($Page, $PageID)
  390. {
  391. $QueryID = G::$DB->get_query_id();
  392. G::$DB->query("
  393. SELECT UserID
  394. FROM users_notify_quoted
  395. WHERE Page = '$Page'
  396. AND PageID = $PageID");
  397. $Subscribers = G::$DB->collect('UserID');
  398. foreach ($Subscribers as $Subscriber) {
  399. G::$Cache->delete_value("notify_quoted_$Subscriber");
  400. }
  401. G::$DB->set_query_id($QueryID);
  402. }
  403. }