Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
 
25
namespace mod_forum;
26
 
27
defined('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
 */
35
class subscriptions {
36
 
37
    /**
38
     * The status value for an unsubscribed discussion.
39
     *
40
     * @var int
41
     */
42
    const FORUM_DISCUSSION_UNSUBSCRIBED = -1;
43
 
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
    /**
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.
96
     *
97
     * @param int $userid The user ID
98
     * @param \stdClass $forum The record of the forum to test
99
     * @param int $discussionid The ID of the discussion to check
100
     * @param $cm The coursemodule record. If not supplied, this will be calculated using get_fast_modinfo instead.
101
     * @return boolean
102
     */
103
    public static function is_subscribed($userid, $forum, $discussionid = null, $cm = null) {
104
        // If forum is force subscribed and has allowforcesubscribe, then user is subscribed.
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
            }
112
        }
113
 
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])) {
122
            return ($subscriptions[$discussionid] != self::FORUM_DISCUSSION_UNSUBSCRIBED);
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) {
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) {
167
        return (isloggedin() && !isguestuser() &&
168
                !\mod_forum\subscriptions::is_forcesubscribed($forum) &&
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 true
180
     * @throws dml_exception A DML specific exception is thrown for any errors.
181
     */
182
    public static function set_subscription_mode($forum, $status = FORUM_FORCESUBSCRIBE): bool {
183
        global $DB;
184
 
185
        if (is_numeric($forum)) {
186
            debugging(__METHOD__.': Argument #1 ($forum) must be a stdClass record of a forum', DEBUG_DEVELOPER);
187
 
188
            $forum = $DB->get_record("forum", ["id" => $forum], '*', MUST_EXIST);
189
        }
190
 
191
        $DB->set_field("forum", "forcesubscribe", $status, ["id" => $forum->id]);
192
 
193
        if ($forum->forcesubscribe != $status) {
194
            // Trigger event if subscription mode has been changed.
195
            $event = \mod_forum\event\subscription_mode_updated::create([
196
                "context" => forum_get_context($forum->id),
197
                "objectid" => $forum->id,
198
                "other" => ["oldvalue" => $forum->forcesubscribe, "newvalue" => $status],
199
            ]);
200
            $event->add_record_snapshot("forum", $forum);
201
            $event->trigger();
202
        }
203
 
204
        return true;
205
    }
206
 
207
    /**
208
     * Returns the current subscription mode for the forum.
209
     *
210
     * @param \stdClass $forum The record of the forum to set
211
     * @return int The forum subscription mode
212
     */
213
    public static function get_subscription_mode($forum) {
214
        return $forum->forcesubscribe;
215
    }
216
 
217
    /**
218
     * Returns an array of forums that the current user is subscribed to and is allowed to unsubscribe from
219
     *
220
     * @return array An array of unsubscribable forums
221
     */
222
    public static function get_unsubscribable_forums() {
223
        global $USER, $DB;
224
 
225
        // Get courses that $USER is enrolled in and can see.
226
        $courses = enrol_get_my_courses();
227
        if (empty($courses)) {
228
            return array();
229
        }
230
 
231
        $courseids = array();
232
        foreach($courses as $course) {
233
            $courseids[] = $course->id;
234
        }
235
        list($coursesql, $courseparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED, 'c');
236
 
237
        // Get all forums from the user's courses that they are subscribed to and which are not set to forced.
238
        // It is possible for users to be subscribed to a forum in subscription disallowed mode so they must be listed
239
        // here so that that can be unsubscribed from.
240
        $sql = "SELECT f.id, cm.id as cm, cm.visible, f.course
241
                FROM {forum} f
242
                JOIN {course_modules} cm ON cm.instance = f.id
243
                JOIN {modules} m ON m.name = :modulename AND m.id = cm.module
244
                LEFT JOIN {forum_subscriptions} fs ON (fs.forum = f.id AND fs.userid = :userid)
245
                WHERE f.forcesubscribe <> :forcesubscribe
246
                AND fs.id IS NOT NULL
247
                AND cm.course
248
                $coursesql";
249
        $params = array_merge($courseparams, array(
250
            'modulename'=>'forum',
251
            'userid' => $USER->id,
252
            'forcesubscribe' => FORUM_FORCESUBSCRIBE,
253
        ));
254
        $forums = $DB->get_recordset_sql($sql, $params);
255
 
256
        $unsubscribableforums = array();
257
        foreach($forums as $forum) {
258
            if (empty($forum->visible)) {
259
                // The forum is hidden - check if the user can view the forum.
260
                $context = \context_module::instance($forum->cm);
261
                if (!has_capability('moodle/course:viewhiddenactivities', $context)) {
262
                    // The user can't see the hidden forum to cannot unsubscribe.
263
                    continue;
264
                }
265
            }
266
 
267
            $unsubscribableforums[] = $forum;
268
        }
269
        $forums->close();
270
 
271
        return $unsubscribableforums;
272
    }
273
 
274
    /**
275
     * Get the list of potential subscribers to a forum.
276
     *
277
     * @param context_module $context the forum context.
278
     * @param integer $groupid the id of a group, or 0 for all groups.
279
     * @param string $fields the list of fields to return for each user. As for get_users_by_capability.
280
     * @param string $sort sort order. As for get_users_by_capability.
281
     * @return array list of users.
282
     */
283
    public static function get_potential_subscribers($context, $groupid, $fields, $sort = '') {
284
        global $DB;
285
 
286
        // Only active enrolled users or everybody on the frontpage.
287
        list($esql, $params) = get_enrolled_sql($context, 'mod/forum:allowforcesubscribe', $groupid, true);
288
        if (!$sort) {
289
            list($sort, $sortparams) = users_order_by_sql('u');
290
            $params = array_merge($params, $sortparams);
291
        }
292
 
293
        $sql = "SELECT $fields
294
                FROM {user} u
295
                JOIN ($esql) je ON je.id = u.id
296
               WHERE u.auth <> 'nologin' AND u.suspended = 0 AND u.confirmed = 1
297
            ORDER BY $sort";
298
 
299
        return $DB->get_records_sql($sql, $params);
300
    }
301
 
302
    /**
303
     * Fetch the forum subscription data for the specified userid and forum.
304
     *
305
     * @param int $forumid The forum to retrieve a cache for
306
     * @param int $userid The user ID
307
     * @return boolean
308
     */
309
    public static function fetch_subscription_cache($forumid, $userid) {
310
        if (isset(self::$forumcache[$userid]) && isset(self::$forumcache[$userid][$forumid])) {
311
            return self::$forumcache[$userid][$forumid];
312
        }
313
        self::fill_subscription_cache($forumid, $userid);
314
 
315
        if (!isset(self::$forumcache[$userid]) || !isset(self::$forumcache[$userid][$forumid])) {
316
            return false;
317
        }
318
 
319
        return self::$forumcache[$userid][$forumid];
320
    }
321
 
322
    /**
323
     * Fill the forum subscription data for the specified userid and forum.
324
     *
325
     * If the userid is not specified, then all subscription data for that forum is fetched in a single query and used
326
     * for subsequent lookups without requiring further database queries.
327
     *
328
     * @param int $forumid The forum to retrieve a cache for
329
     * @param int $userid The user ID
330
     * @return void
331
     */
332
    public static function fill_subscription_cache($forumid, $userid = null) {
333
        global $DB;
334
 
335
        if (!isset(self::$fetchedforums[$forumid])) {
336
            // This forum has not been fetched as a whole.
337
            if (isset($userid)) {
338
                if (!isset(self::$forumcache[$userid])) {
339
                    self::$forumcache[$userid] = array();
340
                }
341
 
342
                if (!isset(self::$forumcache[$userid][$forumid])) {
343
                    if ($DB->record_exists('forum_subscriptions', array(
344
                        'userid' => $userid,
345
                        'forum' => $forumid,
346
                    ))) {
347
                        self::$forumcache[$userid][$forumid] = true;
348
                    } else {
349
                        self::$forumcache[$userid][$forumid] = false;
350
                    }
351
                }
352
            } else {
353
                $subscriptions = $DB->get_recordset('forum_subscriptions', array(
354
                    'forum' => $forumid,
355
                ), '', 'id, userid');
356
                foreach ($subscriptions as $id => $data) {
357
                    if (!isset(self::$forumcache[$data->userid])) {
358
                        self::$forumcache[$data->userid] = array();
359
                    }
360
                    self::$forumcache[$data->userid][$forumid] = true;
361
                }
362
                self::$fetchedforums[$forumid] = true;
363
                $subscriptions->close();
364
            }
365
        }
366
    }
367
 
368
    /**
369
     * Fill the forum subscription data for all forums that the specified userid can subscribe to in the specified course.
370
     *
371
     * @param int $courseid The course to retrieve a cache for
372
     * @param int $userid The user ID
373
     * @return void
374
     */
375
    public static function fill_subscription_cache_for_course($courseid, $userid) {
376
        global $DB;
377
 
378
        if (!isset(self::$forumcache[$userid])) {
379
            self::$forumcache[$userid] = array();
380
        }
381
 
382
        $sql = "SELECT
383
                    f.id AS forumid,
384
                    s.id AS subscriptionid
385
                FROM {forum} f
386
                LEFT JOIN {forum_subscriptions} s ON (s.forum = f.id AND s.userid = :userid)
387
                WHERE f.course = :course
388
                AND f.forcesubscribe <> :subscriptionforced";
389
 
390
        $subscriptions = $DB->get_recordset_sql($sql, array(
391
            'course' => $courseid,
392
            'userid' => $userid,
393
            'subscriptionforced' => FORUM_FORCESUBSCRIBE,
394
        ));
395
 
396
        foreach ($subscriptions as $id => $data) {
397
            self::$forumcache[$userid][$id] = !empty($data->subscriptionid);
398
        }
399
        $subscriptions->close();
400
    }
401
 
402
    /**
403
     * Returns a list of user objects who are subscribed to this forum.
404
     *
405
     * @param stdClass $forum The forum record.
406
     * @param int $groupid The group id if restricting subscriptions to a group of users, or 0 for all.
407
     * @param context_module $context the forum context, to save re-fetching it where possible.
408
     * @param string $fields requested user fields (with "u." table prefix).
409
     * @param boolean $includediscussionsubscriptions Whether to take discussion subscriptions and unsubscriptions into consideration.
410
     * @return array list of users.
411
     */
412
    public static function fetch_subscribed_users($forum, $groupid = 0, $context = null, $fields = null,
413
            $includediscussionsubscriptions = false) {
414
        global $CFG, $DB;
415
 
416
        if (empty($fields)) {
417
            $userfieldsapi = \core_user\fields::for_name();
418
            $allnames = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
419
            $fields ="u.id,
420
                      u.username,
421
                      $allnames,
422
                      u.maildisplay,
423
                      u.mailformat,
424
                      u.maildigest,
425
                      u.imagealt,
426
                      u.email,
427
                      u.emailstop,
428
                      u.city,
429
                      u.country,
430
                      u.lastaccess,
431
                      u.lastlogin,
432
                      u.picture,
433
                      u.timezone,
434
                      u.theme,
435
                      u.lang,
436
                      u.trackforums,
437
                      u.mnethostid";
438
        }
439
 
440
        // Retrieve the forum context if it wasn't specified.
441
        $context = forum_get_context($forum->id, $context);
442
        if (self::is_forcesubscribed($forum)) {
443
            $results = self::get_potential_subscribers($context, $groupid, $fields);
444
 
445
        } else {
446
            // Only active enrolled users or everybody on the frontpage.
447
            list($esql, $params) = get_enrolled_sql($context, '', $groupid, true);
448
            $params['forumid'] = $forum->id;
449
 
450
            list($sort, $sortparams) = users_order_by_sql('u');
451
            $params = array_merge($params, $sortparams);
452
 
453
            if ($includediscussionsubscriptions) {
454
                $params['sforumid'] = $forum->id;
455
                $params['dsforumid'] = $forum->id;
456
                $params['unsubscribed'] = self::FORUM_DISCUSSION_UNSUBSCRIBED;
457
 
458
                $sql = "SELECT $fields
459
                        FROM (
460
                            SELECT userid FROM {forum_subscriptions} s
461
                            WHERE
462
                                s.forum = :sforumid
463
                                UNION
464
                            SELECT userid FROM {forum_discussion_subs} ds
465
                            WHERE
466
                                ds.forum = :dsforumid AND ds.preference <> :unsubscribed
467
                        ) subscriptions
468
                        JOIN {user} u ON u.id = subscriptions.userid
469
                        JOIN ($esql) je ON je.id = u.id
470
                        WHERE u.auth <> 'nologin' AND u.suspended = 0 AND u.confirmed = 1
471
                        ORDER BY $sort";
472
 
473
            } else {
474
                $sql = "SELECT $fields
475
                        FROM {user} u
476
                        JOIN ($esql) je ON je.id = u.id
477
                        JOIN {forum_subscriptions} s ON s.userid = u.id
478
                        WHERE
479
                          s.forum = :forumid AND u.auth <> 'nologin' AND u.suspended = 0 AND u.confirmed = 1
480
                        ORDER BY $sort";
481
            }
482
            $results = $DB->get_records_sql($sql, $params);
483
        }
484
 
485
        // Guest user should never be subscribed to a forum.
486
        unset($results[$CFG->siteguest]);
487
 
488
        // Apply the activity module availability resetrictions.
489
        $cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course);
490
        $modinfo = get_fast_modinfo($forum->course);
491
        $info = new \core_availability\info_module($modinfo->get_cm($cm->id));
492
        $results = $info->filter_user_list($results);
493
 
494
        return $results;
495
    }
496
 
497
    /**
498
     * Retrieve the discussion subscription data for the specified userid and forum.
499
     *
500
     * This is returned as an array of discussions for that forum which contain the preference in a stdClass.
501
     *
502
     * @param int $forumid The forum to retrieve a cache for
503
     * @param int $userid The user ID
504
     * @return array of stdClass objects with one per discussion in the forum.
505
     */
506
    public static function fetch_discussion_subscription($forumid, $userid = null) {
507
        self::fill_discussion_subscription_cache($forumid, $userid);
508
 
509
        if (!isset(self::$forumdiscussioncache[$userid]) || !isset(self::$forumdiscussioncache[$userid][$forumid])) {
510
            return array();
511
        }
512
 
513
        return self::$forumdiscussioncache[$userid][$forumid];
514
    }
515
 
516
    /**
517
     * Fill the discussion subscription data for the specified userid and forum.
518
     *
519
     * If the userid is not specified, then all discussion subscription data for that forum is fetched in a single query
520
     * and used for subsequent lookups without requiring further database queries.
521
     *
522
     * @param int $forumid The forum to retrieve a cache for
523
     * @param int $userid The user ID
524
     * @return void
525
     */
526
    public static function fill_discussion_subscription_cache($forumid, $userid = null) {
527
        global $DB;
528
 
529
        if (!isset(self::$discussionfetchedforums[$forumid])) {
530
            // This forum hasn't been fetched as a whole yet.
531
            if (isset($userid)) {
532
                if (!isset(self::$forumdiscussioncache[$userid])) {
533
                    self::$forumdiscussioncache[$userid] = array();
534
                }
535
 
536
                if (!isset(self::$forumdiscussioncache[$userid][$forumid])) {
537
                    $subscriptions = $DB->get_recordset('forum_discussion_subs', array(
538
                        'userid' => $userid,
539
                        'forum' => $forumid,
540
                    ), null, 'id, discussion, preference');
541
 
542
                    self::$forumdiscussioncache[$userid][$forumid] = array();
543
                    foreach ($subscriptions as $id => $data) {
544
                        self::add_to_discussion_cache($forumid, $userid, $data->discussion, $data->preference);
545
                    }
546
 
547
                    $subscriptions->close();
548
                }
549
            } else {
550
                $subscriptions = $DB->get_recordset('forum_discussion_subs', array(
551
                    'forum' => $forumid,
552
                ), null, 'id, userid, discussion, preference');
553
                foreach ($subscriptions as $id => $data) {
554
                    self::add_to_discussion_cache($forumid, $data->userid, $data->discussion, $data->preference);
555
                }
556
                self::$discussionfetchedforums[$forumid] = true;
557
                $subscriptions->close();
558
            }
559
        }
560
    }
561
 
562
    /**
563
     * Add the specified discussion and user preference to the discussion
564
     * subscription cache.
565
     *
566
     * @param int $forumid The ID of the forum that this preference belongs to
567
     * @param int $userid The ID of the user that this preference belongs to
568
     * @param int $discussion The ID of the discussion that this preference relates to
569
     * @param int $preference The preference to store
570
     */
571
    protected static function add_to_discussion_cache($forumid, $userid, $discussion, $preference) {
572
        if (!isset(self::$forumdiscussioncache[$userid])) {
573
            self::$forumdiscussioncache[$userid] = array();
574
        }
575
 
576
        if (!isset(self::$forumdiscussioncache[$userid][$forumid])) {
577
            self::$forumdiscussioncache[$userid][$forumid] = array();
578
        }
579
 
580
        self::$forumdiscussioncache[$userid][$forumid][$discussion] = $preference;
581
    }
582
 
583
    /**
584
     * Reset the discussion cache.
585
     *
586
     * This cache is used to reduce the number of database queries when
587
     * checking forum discussion subscription states.
588
     */
589
    public static function reset_discussion_cache() {
590
        self::$forumdiscussioncache = array();
591
        self::$discussionfetchedforums = array();
592
    }
593
 
594
    /**
595
     * Reset the forum cache.
596
     *
597
     * This cache is used to reduce the number of database queries when
598
     * checking forum subscription states.
599
     */
600
    public static function reset_forum_cache() {
601
        self::$forumcache = array();
602
        self::$fetchedforums = array();
603
    }
604
 
605
    /**
606
     * Adds user to the subscriber list.
607
     *
608
     * @param int $userid The ID of the user to subscribe
609
     * @param \stdClass $forum The forum record for this forum.
610
     * @param \context_module|null $context Module context, may be omitted if not known or if called for the current
611
     *      module set in page.
612
     * @param boolean $userrequest Whether the user requested this change themselves. This has an effect on whether
613
     *     discussion subscriptions are removed too.
614
     * @return bool|int Returns true if the user is already subscribed, or the forum_subscriptions ID if the user was
615
     *     successfully subscribed.
616
     */
617
    public static function subscribe_user($userid, $forum, $context = null, $userrequest = false) {
618
        global $DB;
619
 
620
        if (self::is_subscribed($userid, $forum)) {
621
            return true;
622
        }
623
 
624
        $sub = new \stdClass();
625
        $sub->userid  = $userid;
626
        $sub->forum = $forum->id;
627
 
628
        $result = $DB->insert_record("forum_subscriptions", $sub);
629
 
630
        if ($userrequest) {
631
            $discussionsubscriptions = $DB->get_recordset('forum_discussion_subs', array('userid' => $userid, 'forum' => $forum->id));
632
            $DB->delete_records_select('forum_discussion_subs',
633
                    'userid = :userid AND forum = :forumid AND preference <> :preference', array(
634
                        'userid' => $userid,
635
                        'forumid' => $forum->id,
636
                        'preference' => self::FORUM_DISCUSSION_UNSUBSCRIBED,
637
                    ));
638
 
639
            // Reset the subscription caches for this forum.
640
            // We know that the there were previously entries and there aren't any more.
641
            if (isset(self::$forumdiscussioncache[$userid]) && isset(self::$forumdiscussioncache[$userid][$forum->id])) {
642
                foreach (self::$forumdiscussioncache[$userid][$forum->id] as $discussionid => $preference) {
643
                    if ($preference != self::FORUM_DISCUSSION_UNSUBSCRIBED) {
644
                        unset(self::$forumdiscussioncache[$userid][$forum->id][$discussionid]);
645
                    }
646
                }
647
            }
648
        }
649
 
650
        // Reset the cache for this forum.
651
        self::$forumcache[$userid][$forum->id] = true;
652
 
653
        $context = forum_get_context($forum->id, $context);
654
        $params = array(
655
            'context' => $context,
656
            'objectid' => $result,
657
            'relateduserid' => $userid,
658
            'other' => array('forumid' => $forum->id),
659
 
660
        );
661
        $event  = event\subscription_created::create($params);
662
        if ($userrequest && $discussionsubscriptions) {
663
            foreach ($discussionsubscriptions as $subscription) {
664
                $event->add_record_snapshot('forum_discussion_subs', $subscription);
665
            }
666
            $discussionsubscriptions->close();
667
        }
668
        $event->trigger();
669
 
670
        return $result;
671
    }
672
 
673
    /**
674
     * Removes user from the subscriber list
675
     *
676
     * @param int $userid The ID of the user to unsubscribe
677
     * @param \stdClass $forum The forum record for this forum.
678
     * @param \context_module|null $context Module context, may be omitted if not known or if called for the current
679
     *     module set in page.
680
     * @param boolean $userrequest Whether the user requested this change themselves. This has an effect on whether
681
     *     discussion subscriptions are removed too.
682
     * @return boolean Always returns true.
683
     */
684
    public static function unsubscribe_user($userid, $forum, $context = null, $userrequest = false) {
685
        global $DB;
686
 
687
        $sqlparams = array(
688
            'userid' => $userid,
689
            'forum' => $forum->id,
690
        );
691
        $DB->delete_records('forum_digests', $sqlparams);
692
 
693
        if ($forumsubscription = $DB->get_record('forum_subscriptions', $sqlparams)) {
694
            $DB->delete_records('forum_subscriptions', array('id' => $forumsubscription->id));
695
 
696
            if ($userrequest) {
697
                $discussionsubscriptions = $DB->get_recordset('forum_discussion_subs', $sqlparams);
698
                $DB->delete_records('forum_discussion_subs',
699
                        array('userid' => $userid, 'forum' => $forum->id, 'preference' => self::FORUM_DISCUSSION_UNSUBSCRIBED));
700
 
701
                // We know that the there were previously entries and there aren't any more.
702
                if (isset(self::$forumdiscussioncache[$userid]) && isset(self::$forumdiscussioncache[$userid][$forum->id])) {
703
                    self::$forumdiscussioncache[$userid][$forum->id] = array();
704
                }
705
            }
706
 
707
            // Reset the cache for this forum.
708
            self::$forumcache[$userid][$forum->id] = false;
709
 
710
            $context = forum_get_context($forum->id, $context);
711
            $params = array(
712
                'context' => $context,
713
                'objectid' => $forumsubscription->id,
714
                'relateduserid' => $userid,
715
                'other' => array('forumid' => $forum->id),
716
 
717
            );
718
            $event = event\subscription_deleted::create($params);
719
            $event->add_record_snapshot('forum_subscriptions', $forumsubscription);
720
            if ($userrequest && $discussionsubscriptions) {
721
                foreach ($discussionsubscriptions as $subscription) {
722
                    $event->add_record_snapshot('forum_discussion_subs', $subscription);
723
                }
724
                $discussionsubscriptions->close();
725
            }
726
            $event->trigger();
727
        }
728
 
729
        return true;
730
    }
731
 
732
    /**
733
     * Subscribes the user to the specified discussion.
734
     *
735
     * @param int $userid The userid of the user being subscribed
736
     * @param \stdClass $discussion The discussion to subscribe to
737
     * @param \context_module|null $context Module context, may be omitted if not known or if called for the current
738
     *     module set in page.
739
     * @return boolean Whether a change was made
740
     */
741
    public static function subscribe_user_to_discussion($userid, $discussion, $context = null) {
742
        global $DB;
743
 
744
        // First check whether the user is subscribed to the discussion already.
745
        $subscription = $DB->get_record('forum_discussion_subs', array('userid' => $userid, 'discussion' => $discussion->id));
746
        if ($subscription) {
747
            if ($subscription->preference != self::FORUM_DISCUSSION_UNSUBSCRIBED) {
748
                // The user is already subscribed to the discussion. Ignore.
749
                return false;
750
            }
751
        }
752
        // No discussion-level subscription. Check for a forum level subscription.
753
        if ($DB->record_exists('forum_subscriptions', array('userid' => $userid, 'forum' => $discussion->forum))) {
754
            if ($subscription && $subscription->preference == self::FORUM_DISCUSSION_UNSUBSCRIBED) {
755
                // The user is subscribed to the forum, but unsubscribed from the discussion, delete the discussion preference.
756
                $DB->delete_records('forum_discussion_subs', array('id' => $subscription->id));
757
                unset(self::$forumdiscussioncache[$userid][$discussion->forum][$discussion->id]);
758
            } else {
759
                // The user is already subscribed to the forum. Ignore.
760
                return false;
761
            }
762
        } else {
763
            if ($subscription) {
764
                $subscription->preference = time();
765
                $DB->update_record('forum_discussion_subs', $subscription);
766
            } else {
767
                $subscription = new \stdClass();
768
                $subscription->userid  = $userid;
769
                $subscription->forum = $discussion->forum;
770
                $subscription->discussion = $discussion->id;
771
                $subscription->preference = time();
772
 
773
                $subscription->id = $DB->insert_record('forum_discussion_subs', $subscription);
774
                self::$forumdiscussioncache[$userid][$discussion->forum][$discussion->id] = $subscription->preference;
775
            }
776
        }
777
 
778
        $context = forum_get_context($discussion->forum, $context);
779
        $params = array(
780
            'context' => $context,
781
            'objectid' => $subscription->id,
782
            'relateduserid' => $userid,
783
            'other' => array(
784
                'forumid' => $discussion->forum,
785
                'discussion' => $discussion->id,
786
            ),
787
 
788
        );
789
        $event  = event\discussion_subscription_created::create($params);
790
        $event->trigger();
791
 
792
        return true;
793
    }
794
    /**
795
     * Unsubscribes the user from the specified discussion.
796
     *
797
     * @param int $userid The userid of the user being unsubscribed
798
     * @param \stdClass $discussion The discussion to unsubscribe from
799
     * @param \context_module|null $context Module context, may be omitted if not known or if called for the current
800
     *     module set in page.
801
     * @return boolean Whether a change was made
802
     */
803
    public static function unsubscribe_user_from_discussion($userid, $discussion, $context = null) {
804
        global $DB;
805
 
806
        // First check whether the user's subscription preference for this discussion.
807
        $subscription = $DB->get_record('forum_discussion_subs', array('userid' => $userid, 'discussion' => $discussion->id));
808
        if ($subscription) {
809
            if ($subscription->preference == self::FORUM_DISCUSSION_UNSUBSCRIBED) {
810
                // The user is already unsubscribed from the discussion. Ignore.
811
                return false;
812
            }
813
        }
814
        // No discussion-level preference. Check for a forum level subscription.
815
        if (!$DB->record_exists('forum_subscriptions', array('userid' => $userid, 'forum' => $discussion->forum))) {
816
            if ($subscription && $subscription->preference != self::FORUM_DISCUSSION_UNSUBSCRIBED) {
817
                // The user is not subscribed to the forum, but subscribed from the discussion, delete the discussion subscription.
818
                $DB->delete_records('forum_discussion_subs', array('id' => $subscription->id));
819
                unset(self::$forumdiscussioncache[$userid][$discussion->forum][$discussion->id]);
820
            } else {
821
                // The user is not subscribed from the forum. Ignore.
822
                return false;
823
            }
824
        } else {
825
            if ($subscription) {
826
                $subscription->preference = self::FORUM_DISCUSSION_UNSUBSCRIBED;
827
                $DB->update_record('forum_discussion_subs', $subscription);
828
            } else {
829
                $subscription = new \stdClass();
830
                $subscription->userid  = $userid;
831
                $subscription->forum = $discussion->forum;
832
                $subscription->discussion = $discussion->id;
833
                $subscription->preference = self::FORUM_DISCUSSION_UNSUBSCRIBED;
834
 
835
                $subscription->id = $DB->insert_record('forum_discussion_subs', $subscription);
836
            }
837
            self::$forumdiscussioncache[$userid][$discussion->forum][$discussion->id] = $subscription->preference;
838
        }
839
 
840
        $context = forum_get_context($discussion->forum, $context);
841
        $params = array(
842
            'context' => $context,
843
            'objectid' => $subscription->id,
844
            'relateduserid' => $userid,
845
            'other' => array(
846
                'forumid' => $discussion->forum,
847
                'discussion' => $discussion->id,
848
            ),
849
 
850
        );
851
        $event  = event\discussion_subscription_deleted::create($params);
852
        $event->trigger();
853
 
854
        return true;
855
    }
856
 
857
    /**
858
     * Gets the default subscription value for the logged in user.
859
     *
860
     * @param \stdClass $forum The forum record
861
     * @param \context $context The course context
862
     * @param \cm_info $cm cm_info
863
     * @param int|null $discussionid The discussion we are checking against
864
     * @return bool Default subscription
865
     * @throws coding_exception
866
     */
867
    public static function get_user_default_subscription($forum, $context, $cm, ?int $discussionid) {
868
        global $USER;
869
        $manageactivities = has_capability('moodle/course:manageactivities', $context);
870
        if (\mod_forum\subscriptions::subscription_disabled($forum) && !$manageactivities) {
871
            // User does not have permission to subscribe to this discussion at all.
872
            $discussionsubscribe = false;
873
        } else if (\mod_forum\subscriptions::is_forcesubscribed($forum)) {
874
            // User does not have permission to unsubscribe from this discussion at all.
875
            $discussionsubscribe = true;
876
        } else {
877
            if (isset($discussionid) && self::is_subscribed($USER->id, $forum, $discussionid, $cm)) {
878
                // User is subscribed to the discussion - continue the subscription.
879
                $discussionsubscribe = true;
880
            } else if (!isset($discussionid) && \mod_forum\subscriptions::is_subscribed($USER->id, $forum, null, $cm)) {
881
                // Starting a new discussion, and the user is subscribed to the forum - subscribe to the discussion.
882
                $discussionsubscribe = true;
883
            } else {
884
                // User is not subscribed to either forum or discussion. Follow user preference.
885
                $discussionsubscribe = $USER->autosubscribe ?? false;
886
            }
887
        }
888
 
889
        return $discussionsubscribe;
890
    }
891
}