MDL-65069 mod_forum: Remove unused code and additional dev docs.
[moodle.git] / mod / forum / classes / subscriptions.php
CommitLineData
59075a43
AN
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Forum subscription manager.
19 *
20 * @package mod_forum
21 * @copyright 2014 Andrew Nicols <andrew@nicols.co.uk>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
25namespace mod_forum;
26
27defined('MOODLE_INTERNAL') || die();
28
29/**
30 * Forum subscription manager.
31 *
32 * @copyright 2014 Andrew Nicols <andrew@nicols.co.uk>
33 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
34 */
35class subscriptions {
e3bbfb52 36
e3bbfb52
AN
37 /**
38 * The status value for an unsubscribed discussion.
39 *
40 * @var int
41 */
42 const FORUM_DISCUSSION_UNSUBSCRIBED = -1;
43
59075a43
AN
44 /**
45 * The subscription cache for forums.
46 *
47 * The first level key is the user ID
48 * The second level is the forum ID
49 * The Value then is bool for subscribed of not.
50 *
51 * @var array[] An array of arrays.
52 */
53 protected static $forumcache = array();
54
55 /**
56 * The list of forums which have been wholly retrieved for the forum subscription cache.
57 *
58 * This allows for prior caching of an entire forum to reduce the
59 * number of DB queries in a subscription check loop.
60 *
61 * @var bool[]
62 */
63 protected static $fetchedforums = array();
64
65 /**
e3bbfb52
AN
66 * The subscription cache for forum discussions.
67 *
68 * The first level key is the user ID
69 * The second level is the forum ID
70 * The third level key is the discussion ID
71 * The value is then the users preference (int)
72 *
73 * @var array[]
74 */
75 protected static $forumdiscussioncache = array();
76
77 /**
78 * The list of forums which have been wholly retrieved for the forum discussion subscription cache.
79 *
80 * This allows for prior caching of an entire forum to reduce the
81 * number of DB queries in a subscription check loop.
82 *
83 * @var bool[]
84 */
85 protected static $discussionfetchedforums = array();
86
87 /**
88 * Whether a user is subscribed to this forum, or a discussion within
89 * the forum.
90 *
91 * If a discussion is specified, then report whether the user is
92 * subscribed to posts to this particular discussion, taking into
93 * account the forum preference.
94 *
95 * If it is not specified then only the forum preference is considered.
59075a43
AN
96 *
97 * @param int $userid The user ID
98 * @param \stdClass $forum The record of the forum to test
e3bbfb52 99 * @param int $discussionid The ID of the discussion to check
83daad5a 100 * @param $cm The coursemodule record. If not supplied, this will be calculated using get_fast_modinfo instead.
59075a43
AN
101 * @return boolean
102 */
83daad5a 103 public static function is_subscribed($userid, $forum, $discussionid = null, $cm = null) {
59075a43 104 // If forum is force subscribed and has allowforcesubscribe, then user is subscribed.
4238983e
AN
105 if (self::is_forcesubscribed($forum)) {
106 if (!$cm) {
107 $cm = get_fast_modinfo($forum->course)->instances['forum'][$forum->id];
108 }
109 if (has_capability('mod/forum:allowforcesubscribe', \context_module::instance($cm->id), $userid)) {
110 return true;
111 }
59075a43
AN
112 }
113
e3bbfb52
AN
114 if ($discussionid === null) {
115 return self::is_subscribed_to_forum($userid, $forum);
116 }
117
118 $subscriptions = self::fetch_discussion_subscription($forum->id, $userid);
119
120 // Check whether there is a record for this discussion subscription.
121 if (isset($subscriptions[$discussionid])) {
eb451c79 122 return ($subscriptions[$discussionid] != self::FORUM_DISCUSSION_UNSUBSCRIBED);
e3bbfb52
AN
123 }
124
125 return self::is_subscribed_to_forum($userid, $forum);
126 }
127
128 /**
129 * Whether a user is subscribed to this forum.
130 *
131 * @param int $userid The user ID
132 * @param \stdClass $forum The record of the forum to test
133 * @return boolean
134 */
135 protected static function is_subscribed_to_forum($userid, $forum) {
59075a43
AN
136 return self::fetch_subscription_cache($forum->id, $userid);
137 }
138
139 /**
140 * Helper to determine whether a forum has it's subscription mode set
141 * to forced subscription.
142 *
143 * @param \stdClass $forum The record of the forum to test
144 * @return bool
145 */
146 public static function is_forcesubscribed($forum) {
147 return ($forum->forcesubscribe == FORUM_FORCESUBSCRIBE);
148 }
149
150 /**
151 * Helper to determine whether a forum has it's subscription mode set to disabled.
152 *
153 * @param \stdClass $forum The record of the forum to test
154 * @return bool
155 */
156 public static function subscription_disabled($forum) {
157 return ($forum->forcesubscribe == FORUM_DISALLOWSUBSCRIBE);
158 }
159
160 /**
161 * Helper to determine whether the specified forum can be subscribed to.
162 *
163 * @param \stdClass $forum The record of the forum to test
164 * @return bool
165 */
166 public static function is_subscribable($forum) {
8381ac52
AN
167 return (isloggedin() && !isguestuser() &&
168 !\mod_forum\subscriptions::is_forcesubscribed($forum) &&
59075a43
AN
169 !\mod_forum\subscriptions::subscription_disabled($forum));
170 }
171
172 /**
173 * Set the forum subscription mode.
174 *
175 * By default when called without options, this is set to FORUM_FORCESUBSCRIBE.
176 *
177 * @param \stdClass $forum The record of the forum to set
178 * @param int $status The new subscription state
179 * @return bool
180 */
181 public static function set_subscription_mode($forumid, $status = 1) {
182 global $DB;
183 return $DB->set_field("forum", "forcesubscribe", $status, array("id" => $forumid));
184 }
185
186 /**
187 * Returns the current subscription mode for the forum.
188 *
189 * @param \stdClass $forum The record of the forum to set
190 * @return int The forum subscription mode
191 */
192 public static function get_subscription_mode($forum) {
193 return $forum->forcesubscribe;
194 }
195
196 /**
197 * Returns an array of forums that the current user is subscribed to and is allowed to unsubscribe from
198 *
199 * @return array An array of unsubscribable forums
200 */
201 public static function get_unsubscribable_forums() {
202 global $USER, $DB;
203
204 // Get courses that $USER is enrolled in and can see.
205 $courses = enrol_get_my_courses();
206 if (empty($courses)) {
207 return array();
208 }
209
210 $courseids = array();
211 foreach($courses as $course) {
212 $courseids[] = $course->id;
213 }
214 list($coursesql, $courseparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED, 'c');
215
e3bbfb52
AN
216 // Get all forums from the user's courses that they are subscribed to and which are not set to forced.
217 // It is possible for users to be subscribed to a forum in subscription disallowed mode so they must be listed
218 // here so that that can be unsubscribed from.
219 $sql = "SELECT f.id, cm.id as cm, cm.visible, f.course
59075a43 220 FROM {forum} f
e3bbfb52
AN
221 JOIN {course_modules} cm ON cm.instance = f.id
222 JOIN {modules} m ON m.name = :modulename AND m.id = cm.module
223 LEFT JOIN {forum_subscriptions} fs ON (fs.forum = f.id AND fs.userid = :userid)
224 WHERE f.forcesubscribe <> :forcesubscribe
225 AND fs.id IS NOT NULL
226 AND cm.course
227 $coursesql";
59075a43
AN
228 $params = array_merge($courseparams, array(
229 'modulename'=>'forum',
230 'userid' => $USER->id,
231 'forcesubscribe' => FORUM_FORCESUBSCRIBE,
232 ));
233 $forums = $DB->get_recordset_sql($sql, $params);
234
e3bbfb52 235 $unsubscribableforums = array();
59075a43
AN
236 foreach($forums as $forum) {
237 if (empty($forum->visible)) {
238 // The forum is hidden - check if the user can view the forum.
e3bbfb52 239 $context = \context_module::instance($forum->cm);
59075a43
AN
240 if (!has_capability('moodle/course:viewhiddenactivities', $context)) {
241 // The user can't see the hidden forum to cannot unsubscribe.
242 continue;
243 }
244 }
245
246 $unsubscribableforums[] = $forum;
247 }
248 $forums->close();
249
250 return $unsubscribableforums;
251 }
252
253 /**
254 * Get the list of potential subscribers to a forum.
255 *
256 * @param context_module $context the forum context.
257 * @param integer $groupid the id of a group, or 0 for all groups.
258 * @param string $fields the list of fields to return for each user. As for get_users_by_capability.
259 * @param string $sort sort order. As for get_users_by_capability.
260 * @return array list of users.
261 */
262 public static function get_potential_subscribers($context, $groupid, $fields, $sort = '') {
263 global $DB;
264
265 // Only active enrolled users or everybody on the frontpage.
266 list($esql, $params) = get_enrolled_sql($context, 'mod/forum:allowforcesubscribe', $groupid, true);
267 if (!$sort) {
268 list($sort, $sortparams) = users_order_by_sql('u');
269 $params = array_merge($params, $sortparams);
270 }
271
272 $sql = "SELECT $fields
273 FROM {user} u
274 JOIN ($esql) je ON je.id = u.id
275 ORDER BY $sort";
276
277 return $DB->get_records_sql($sql, $params);
278 }
279
280 /**
281 * Fetch the forum subscription data for the specified userid and forum.
282 *
283 * @param int $forumid The forum to retrieve a cache for
284 * @param int $userid The user ID
285 * @return boolean
286 */
287 public static function fetch_subscription_cache($forumid, $userid) {
288 if (isset(self::$forumcache[$userid]) && isset(self::$forumcache[$userid][$forumid])) {
289 return self::$forumcache[$userid][$forumid];
290 }
291 self::fill_subscription_cache($forumid, $userid);
292
293 if (!isset(self::$forumcache[$userid]) || !isset(self::$forumcache[$userid][$forumid])) {
294 return false;
295 }
296
297 return self::$forumcache[$userid][$forumid];
298 }
299
300 /**
301 * Fill the forum subscription data for the specified userid and forum.
302 *
303 * If the userid is not specified, then all subscription data for that forum is fetched in a single query and used
304 * for subsequent lookups without requiring further database queries.
305 *
306 * @param int $forumid The forum to retrieve a cache for
307 * @param int $userid The user ID
308 * @return void
309 */
310 public static function fill_subscription_cache($forumid, $userid = null) {
311 global $DB;
312
313 if (!isset(self::$fetchedforums[$forumid])) {
314 // This forum has not been fetched as a whole.
315 if (isset($userid)) {
316 if (!isset(self::$forumcache[$userid])) {
317 self::$forumcache[$userid] = array();
318 }
319
320 if (!isset(self::$forumcache[$userid][$forumid])) {
321 if ($DB->record_exists('forum_subscriptions', array(
322 'userid' => $userid,
323 'forum' => $forumid,
324 ))) {
325 self::$forumcache[$userid][$forumid] = true;
326 } else {
327 self::$forumcache[$userid][$forumid] = false;
328 }
329 }
330 } else {
331 $subscriptions = $DB->get_recordset('forum_subscriptions', array(
332 'forum' => $forumid,
333 ), '', 'id, userid');
334 foreach ($subscriptions as $id => $data) {
335 if (!isset(self::$forumcache[$data->userid])) {
336 self::$forumcache[$data->userid] = array();
337 }
338 self::$forumcache[$data->userid][$forumid] = true;
339 }
340 self::$fetchedforums[$forumid] = true;
341 $subscriptions->close();
342 }
343 }
344 }
345
346 /**
347 * Fill the forum subscription data for all forums that the specified userid can subscribe to in the specified course.
348 *
349 * @param int $courseid The course to retrieve a cache for
350 * @param int $userid The user ID
351 * @return void
352 */
353 public static function fill_subscription_cache_for_course($courseid, $userid) {
354 global $DB;
355
356 if (!isset(self::$forumcache[$userid])) {
357 self::$forumcache[$userid] = array();
358 }
359
360 $sql = "SELECT
361 f.id AS forumid,
362 s.id AS subscriptionid
363 FROM {forum} f
364 LEFT JOIN {forum_subscriptions} s ON (s.forum = f.id AND s.userid = :userid)
365 WHERE f.course = :course
366 AND f.forcesubscribe <> :subscriptionforced";
367
368 $subscriptions = $DB->get_recordset_sql($sql, array(
369 'course' => $courseid,
370 'userid' => $userid,
371 'subscriptionforced' => FORUM_FORCESUBSCRIBE,
372 ));
373
374 foreach ($subscriptions as $id => $data) {
375 self::$forumcache[$userid][$id] = !empty($data->subscriptionid);
376 }
377 $subscriptions->close();
378 }
379
380 /**
381 * Returns a list of user objects who are subscribed to this forum.
382 *
383 * @param stdClass $forum The forum record.
384 * @param int $groupid The group id if restricting subscriptions to a group of users, or 0 for all.
385 * @param context_module $context the forum context, to save re-fetching it where possible.
386 * @param string $fields requested user fields (with "u." table prefix).
e3bbfb52 387 * @param boolean $includediscussionsubscriptions Whether to take discussion subscriptions and unsubscriptions into consideration.
59075a43
AN
388 * @return array list of users.
389 */
e3bbfb52
AN
390 public static function fetch_subscribed_users($forum, $groupid = 0, $context = null, $fields = null,
391 $includediscussionsubscriptions = false) {
59075a43
AN
392 global $CFG, $DB;
393
394 if (empty($fields)) {
395 $allnames = get_all_user_name_fields(true, 'u');
396 $fields ="u.id,
397 u.username,
398 $allnames,
399 u.maildisplay,
400 u.mailformat,
401 u.maildigest,
402 u.imagealt,
403 u.email,
404 u.emailstop,
405 u.city,
406 u.country,
407 u.lastaccess,
408 u.lastlogin,
409 u.picture,
410 u.timezone,
411 u.theme,
412 u.lang,
413 u.trackforums,
414 u.mnethostid";
415 }
416
417 // Retrieve the forum context if it wasn't specified.
418 $context = forum_get_context($forum->id, $context);
419
420 if (self::is_forcesubscribed($forum)) {
421 $results = \mod_forum\subscriptions::get_potential_subscribers($context, $groupid, $fields, "u.email ASC");
422
423 } else {
424 // Only active enrolled users or everybody on the frontpage.
425 list($esql, $params) = get_enrolled_sql($context, '', $groupid, true);
426 $params['forumid'] = $forum->id;
427
e3bbfb52 428 if ($includediscussionsubscriptions) {
a211c543
AN
429 $params['sforumid'] = $forum->id;
430 $params['dsforumid'] = $forum->id;
e3bbfb52
AN
431 $params['unsubscribed'] = self::FORUM_DISCUSSION_UNSUBSCRIBED;
432
433 $sql = "SELECT $fields
a211c543
AN
434 FROM (
435 SELECT userid FROM {forum_subscriptions} s
436 WHERE
437 s.forum = :sforumid
438 UNION
439 SELECT userid FROM {forum_discussion_subs} ds
e3bbfb52 440 WHERE
a211c543
AN
441 ds.forum = :dsforumid AND ds.preference <> :unsubscribed
442 ) subscriptions
443 JOIN {user} u ON u.id = subscriptions.userid
444 JOIN ($esql) je ON je.id = u.id
e3bbfb52 445 ORDER BY u.email ASC";
a211c543 446
e3bbfb52
AN
447 } else {
448 $sql = "SELECT $fields
449 FROM {user} u
450 JOIN ($esql) je ON je.id = u.id
451 JOIN {forum_subscriptions} s ON s.userid = u.id
452 WHERE
453 s.forum = :forumid
454 ORDER BY u.email ASC";
455 }
59075a43
AN
456 $results = $DB->get_records_sql($sql, $params);
457 }
458
459 // Guest user should never be subscribed to a forum.
460 unset($results[$CFG->siteguest]);
461
c87b87e6
AN
462 // Apply the activity module availability resetrictions.
463 $cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course);
464 $modinfo = get_fast_modinfo($forum->course);
465 $info = new \core_availability\info_module($modinfo->get_cm($cm->id));
466 $results = $info->filter_user_list($results);
467
59075a43
AN
468 return $results;
469 }
470
e3bbfb52
AN
471 /**
472 * Retrieve the discussion subscription data for the specified userid and forum.
473 *
474 * This is returned as an array of discussions for that forum which contain the preference in a stdClass.
475 *
476 * @param int $forumid The forum to retrieve a cache for
477 * @param int $userid The user ID
478 * @return array of stdClass objects with one per discussion in the forum.
479 */
480 public static function fetch_discussion_subscription($forumid, $userid = null) {
481 self::fill_discussion_subscription_cache($forumid, $userid);
482
483 if (!isset(self::$forumdiscussioncache[$userid]) || !isset(self::$forumdiscussioncache[$userid][$forumid])) {
484 return array();
485 }
486
487 return self::$forumdiscussioncache[$userid][$forumid];
488 }
489
490 /**
491 * Fill the discussion subscription data for the specified userid and forum.
492 *
493 * If the userid is not specified, then all discussion subscription data for that forum is fetched in a single query
494 * and used for subsequent lookups without requiring further database queries.
495 *
496 * @param int $forumid The forum to retrieve a cache for
497 * @param int $userid The user ID
498 * @return void
499 */
500 public static function fill_discussion_subscription_cache($forumid, $userid = null) {
501 global $DB;
502
503 if (!isset(self::$discussionfetchedforums[$forumid])) {
504 // This forum hasn't been fetched as a whole yet.
505 if (isset($userid)) {
506 if (!isset(self::$forumdiscussioncache[$userid])) {
507 self::$forumdiscussioncache[$userid] = array();
508 }
509
510 if (!isset(self::$forumdiscussioncache[$userid][$forumid])) {
511 $subscriptions = $DB->get_recordset('forum_discussion_subs', array(
512 'userid' => $userid,
513 'forum' => $forumid,
514 ), null, 'id, discussion, preference');
515 foreach ($subscriptions as $id => $data) {
516 self::add_to_discussion_cache($forumid, $userid, $data->discussion, $data->preference);
517 }
518 $subscriptions->close();
519 }
520 } else {
521 $subscriptions = $DB->get_recordset('forum_discussion_subs', array(
522 'forum' => $forumid,
523 ), null, 'id, userid, discussion, preference');
524 foreach ($subscriptions as $id => $data) {
525 self::add_to_discussion_cache($forumid, $data->userid, $data->discussion, $data->preference);
526 }
527 self::$discussionfetchedforums[$forumid] = true;
528 $subscriptions->close();
529 }
530 }
531 }
532
533 /**
534 * Add the specified discussion and user preference to the discussion
535 * subscription cache.
536 *
537 * @param int $forumid The ID of the forum that this preference belongs to
538 * @param int $userid The ID of the user that this preference belongs to
539 * @param int $discussion The ID of the discussion that this preference relates to
540 * @param int $preference The preference to store
541 */
542 protected static function add_to_discussion_cache($forumid, $userid, $discussion, $preference) {
543 if (!isset(self::$forumdiscussioncache[$userid])) {
544 self::$forumdiscussioncache[$userid] = array();
545 }
546
547 if (!isset(self::$forumdiscussioncache[$userid][$forumid])) {
548 self::$forumdiscussioncache[$userid][$forumid] = array();
549 }
550
551 self::$forumdiscussioncache[$userid][$forumid][$discussion] = $preference;
552 }
553
554 /**
555 * Reset the discussion cache.
556 *
557 * This cache is used to reduce the number of database queries when
558 * checking forum discussion subscription states.
559 */
560 public static function reset_discussion_cache() {
561 self::$forumdiscussioncache = array();
562 self::$discussionfetchedforums = array();
563 }
564
59075a43
AN
565 /**
566 * Reset the forum cache.
567 *
568 * This cache is used to reduce the number of database queries when
569 * checking forum subscription states.
570 */
571 public static function reset_forum_cache() {
572 self::$forumcache = array();
573 self::$fetchedforums = array();
574 }
575
576 /**
577 * Adds user to the subscriber list.
578 *
579 * @param int $userid The ID of the user to subscribe
580 * @param \stdClass $forum The forum record for this forum.
581 * @param \context_module|null $context Module context, may be omitted if not known or if called for the current
582 * module set in page.
e3bbfb52
AN
583 * @param boolean $userrequest Whether the user requested this change themselves. This has an effect on whether
584 * discussion subscriptions are removed too.
59075a43
AN
585 * @return bool|int Returns true if the user is already subscribed, or the forum_subscriptions ID if the user was
586 * successfully subscribed.
587 */
e3bbfb52 588 public static function subscribe_user($userid, $forum, $context = null, $userrequest = false) {
59075a43
AN
589 global $DB;
590
591 if (self::is_subscribed($userid, $forum)) {
592 return true;
593 }
594
595 $sub = new \stdClass();
596 $sub->userid = $userid;
597 $sub->forum = $forum->id;
598
599 $result = $DB->insert_record("forum_subscriptions", $sub);
600
e3bbfb52
AN
601 if ($userrequest) {
602 $discussionsubscriptions = $DB->get_recordset('forum_discussion_subs', array('userid' => $userid, 'forum' => $forum->id));
eb451c79
AN
603 $DB->delete_records_select('forum_discussion_subs',
604 'userid = :userid AND forum = :forumid AND preference <> :preference', array(
605 'userid' => $userid,
606 'forumid' => $forum->id,
607 'preference' => self::FORUM_DISCUSSION_UNSUBSCRIBED,
608 ));
e3bbfb52
AN
609
610 // Reset the subscription caches for this forum.
611 // We know that the there were previously entries and there aren't any more.
612 if (isset(self::$forumdiscussioncache[$userid]) && isset(self::$forumdiscussioncache[$userid][$forum->id])) {
613 foreach (self::$forumdiscussioncache[$userid][$forum->id] as $discussionid => $preference) {
eb451c79 614 if ($preference != self::FORUM_DISCUSSION_UNSUBSCRIBED) {
e3bbfb52
AN
615 unset(self::$forumdiscussioncache[$userid][$forum->id][$discussionid]);
616 }
617 }
618 }
619 }
620
59075a43
AN
621 // Reset the cache for this forum.
622 self::$forumcache[$userid][$forum->id] = true;
623
624 $context = forum_get_context($forum->id, $context);
625 $params = array(
626 'context' => $context,
627 'objectid' => $result,
628 'relateduserid' => $userid,
629 'other' => array('forumid' => $forum->id),
630
631 );
632 $event = event\subscription_created::create($params);
e3bbfb52
AN
633 if ($userrequest && $discussionsubscriptions) {
634 foreach ($discussionsubscriptions as $subscription) {
635 $event->add_record_snapshot('forum_discussion_subs', $subscription);
636 }
637 $discussionsubscriptions->close();
638 }
59075a43
AN
639 $event->trigger();
640
641 return $result;
642 }
643
644 /**
645 * Removes user from the subscriber list
646 *
647 * @param int $userid The ID of the user to unsubscribe
648 * @param \stdClass $forum The forum record for this forum.
649 * @param \context_module|null $context Module context, may be omitted if not known or if called for the current
650 * module set in page.
e3bbfb52
AN
651 * @param boolean $userrequest Whether the user requested this change themselves. This has an effect on whether
652 * discussion subscriptions are removed too.
59075a43
AN
653 * @return boolean Always returns true.
654 */
e3bbfb52 655 public static function unsubscribe_user($userid, $forum, $context = null, $userrequest = false) {
59075a43
AN
656 global $DB;
657
658 $sqlparams = array(
659 'userid' => $userid,
660 'forum' => $forum->id,
661 );
662 $DB->delete_records('forum_digests', $sqlparams);
663
664 if ($forumsubscription = $DB->get_record('forum_subscriptions', $sqlparams)) {
665 $DB->delete_records('forum_subscriptions', array('id' => $forumsubscription->id));
666
e3bbfb52
AN
667 if ($userrequest) {
668 $discussionsubscriptions = $DB->get_recordset('forum_discussion_subs', $sqlparams);
669 $DB->delete_records('forum_discussion_subs',
670 array('userid' => $userid, 'forum' => $forum->id, 'preference' => self::FORUM_DISCUSSION_UNSUBSCRIBED));
671
672 // We know that the there were previously entries and there aren't any more.
673 if (isset(self::$forumdiscussioncache[$userid]) && isset(self::$forumdiscussioncache[$userid][$forum->id])) {
674 self::$forumdiscussioncache[$userid][$forum->id] = array();
675 }
676 }
677
59075a43
AN
678 // Reset the cache for this forum.
679 self::$forumcache[$userid][$forum->id] = false;
680
681 $context = forum_get_context($forum->id, $context);
682 $params = array(
683 'context' => $context,
684 'objectid' => $forumsubscription->id,
685 'relateduserid' => $userid,
686 'other' => array('forumid' => $forum->id),
687
688 );
689 $event = event\subscription_deleted::create($params);
690 $event->add_record_snapshot('forum_subscriptions', $forumsubscription);
e3bbfb52
AN
691 if ($userrequest && $discussionsubscriptions) {
692 foreach ($discussionsubscriptions as $subscription) {
693 $event->add_record_snapshot('forum_discussion_subs', $subscription);
694 }
695 $discussionsubscriptions->close();
696 }
59075a43
AN
697 $event->trigger();
698 }
699
700 return true;
701 }
702
e3bbfb52
AN
703 /**
704 * Subscribes the user to the specified discussion.
705 *
706 * @param int $userid The userid of the user being subscribed
707 * @param \stdClass $discussion The discussion to subscribe to
708 * @param \context_module|null $context Module context, may be omitted if not known or if called for the current
709 * module set in page.
710 * @return boolean Whether a change was made
711 */
712 public static function subscribe_user_to_discussion($userid, $discussion, $context = null) {
713 global $DB;
714
715 // First check whether the user is subscribed to the discussion already.
716 $subscription = $DB->get_record('forum_discussion_subs', array('userid' => $userid, 'discussion' => $discussion->id));
717 if ($subscription) {
eb451c79 718 if ($subscription->preference != self::FORUM_DISCUSSION_UNSUBSCRIBED) {
e3bbfb52
AN
719 // The user is already subscribed to the discussion. Ignore.
720 return false;
721 }
722 }
723 // No discussion-level subscription. Check for a forum level subscription.
724 if ($DB->record_exists('forum_subscriptions', array('userid' => $userid, 'forum' => $discussion->forum))) {
725 if ($subscription && $subscription->preference == self::FORUM_DISCUSSION_UNSUBSCRIBED) {
726 // The user is subscribed to the forum, but unsubscribed from the discussion, delete the discussion preference.
727 $DB->delete_records('forum_discussion_subs', array('id' => $subscription->id));
728 unset(self::$forumdiscussioncache[$userid][$discussion->forum][$discussion->id]);
729 } else {
730 // The user is already subscribed to the forum. Ignore.
731 return false;
732 }
733 } else {
734 if ($subscription) {
eb451c79 735 $subscription->preference = time();
e3bbfb52
AN
736 $DB->update_record('forum_discussion_subs', $subscription);
737 } else {
738 $subscription = new \stdClass();
739 $subscription->userid = $userid;
740 $subscription->forum = $discussion->forum;
741 $subscription->discussion = $discussion->id;
eb451c79 742 $subscription->preference = time();
e3bbfb52
AN
743
744 $subscription->id = $DB->insert_record('forum_discussion_subs', $subscription);
eb451c79 745 self::$forumdiscussioncache[$userid][$discussion->forum][$discussion->id] = $subscription->preference;
e3bbfb52
AN
746 }
747 }
748
749 $context = forum_get_context($discussion->forum, $context);
750 $params = array(
751 'context' => $context,
752 'objectid' => $subscription->id,
753 'relateduserid' => $userid,
754 'other' => array(
755 'forumid' => $discussion->forum,
756 'discussion' => $discussion->id,
757 ),
758
759 );
760 $event = event\discussion_subscription_created::create($params);
761 $event->trigger();
762
763 return true;
764 }
765 /**
766 * Unsubscribes the user from the specified discussion.
767 *
768 * @param int $userid The userid of the user being unsubscribed
769 * @param \stdClass $discussion The discussion to unsubscribe from
770 * @param \context_module|null $context Module context, may be omitted if not known or if called for the current
771 * module set in page.
772 * @return boolean Whether a change was made
773 */
774 public static function unsubscribe_user_from_discussion($userid, $discussion, $context = null) {
775 global $DB;
776
777 // First check whether the user's subscription preference for this discussion.
778 $subscription = $DB->get_record('forum_discussion_subs', array('userid' => $userid, 'discussion' => $discussion->id));
779 if ($subscription) {
780 if ($subscription->preference == self::FORUM_DISCUSSION_UNSUBSCRIBED) {
781 // The user is already unsubscribed from the discussion. Ignore.
782 return false;
783 }
784 }
785 // No discussion-level preference. Check for a forum level subscription.
786 if (!$DB->record_exists('forum_subscriptions', array('userid' => $userid, 'forum' => $discussion->forum))) {
eb451c79 787 if ($subscription && $subscription->preference != self::FORUM_DISCUSSION_UNSUBSCRIBED) {
e3bbfb52
AN
788 // The user is not subscribed to the forum, but subscribed from the discussion, delete the discussion subscription.
789 $DB->delete_records('forum_discussion_subs', array('id' => $subscription->id));
790 unset(self::$forumdiscussioncache[$userid][$discussion->forum][$discussion->id]);
791 } else {
792 // The user is not subscribed from the forum. Ignore.
793 return false;
794 }
795 } else {
796 if ($subscription) {
797 $subscription->preference = self::FORUM_DISCUSSION_UNSUBSCRIBED;
798 $DB->update_record('forum_discussion_subs', $subscription);
799 } else {
800 $subscription = new \stdClass();
801 $subscription->userid = $userid;
802 $subscription->forum = $discussion->forum;
803 $subscription->discussion = $discussion->id;
804 $subscription->preference = self::FORUM_DISCUSSION_UNSUBSCRIBED;
805
806 $subscription->id = $DB->insert_record('forum_discussion_subs', $subscription);
807 }
eb451c79 808 self::$forumdiscussioncache[$userid][$discussion->forum][$discussion->id] = $subscription->preference;
e3bbfb52
AN
809 }
810
811 $context = forum_get_context($discussion->forum, $context);
812 $params = array(
813 'context' => $context,
814 'objectid' => $subscription->id,
815 'relateduserid' => $userid,
816 'other' => array(
817 'forumid' => $discussion->forum,
818 'discussion' => $discussion->id,
819 ),
820
821 );
822 $event = event\discussion_subscription_deleted::create($params);
823 $event->trigger();
824
825 return true;
826 }
827
cb28132d
P
828 /**
829 * Gets the default subscription value for the logged in user.
830 *
831 * @param \stdClass $forum The forum record
832 * @param \context $context The course context
833 * @param \cm_info $cm cm_info
834 * @param int|null $discussionid The discussion we are checking against
835 * @return bool Default subscription
836 * @throws coding_exception
837 */
838 public static function get_user_default_subscription($forum, $context, $cm, ?int $discussionid) {
839 global $USER;
840 $manageactivities = has_capability('moodle/course:manageactivities', $context);
841 if (\mod_forum\subscriptions::subscription_disabled($forum) && !$manageactivities) {
842 // User does not have permission to subscribe to this discussion at all.
843 $discussionsubscribe = false;
844 } else if (\mod_forum\subscriptions::is_forcesubscribed($forum)) {
845 // User does not have permission to unsubscribe from this discussion at all.
846 $discussionsubscribe = true;
847 } else {
848 if (isset($discussion) && \mod_forum\subscriptions::is_subscribed($USER->id, $forum, $discussionid, $cm)) {
849 // User is subscribed to the discussion - continue the subscription.
850 $discussionsubscribe = true;
851 } else if (!isset($discussionid) && \mod_forum\subscriptions::is_subscribed($USER->id, $forum, null, $cm)) {
852 // Starting a new discussion, and the user is subscribed to the forum - subscribe to the discussion.
853 $discussionsubscribe = true;
854 } else {
855 // User is not subscribed to either forum or discussion. Follow user preference.
856 $discussionsubscribe = $USER->autosubscribe ?? false;
857 }
858 }
859
860 return $discussionsubscribe;
861 }
59075a43 862}