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
 * Capability manager for the forum.
19
 *
20
 * @package    mod_forum
21
 * @copyright  2019 Ryan Wyllie <ryan@moodle.com>
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
namespace mod_forum\local\managers;
26
 
27
defined('MOODLE_INTERNAL') || die();
28
 
29
use mod_forum\local\data_mappers\legacy\forum as legacy_forum_data_mapper;
30
use mod_forum\local\data_mappers\legacy\discussion as legacy_discussion_data_mapper;
31
use mod_forum\local\data_mappers\legacy\post as legacy_post_data_mapper;
32
use mod_forum\local\entities\discussion as discussion_entity;
33
use mod_forum\local\entities\forum as forum_entity;
34
use mod_forum\local\entities\post as post_entity;
35
use mod_forum\subscriptions;
36
use context;
37
use context_system;
38
use stdClass;
39
use moodle_exception;
40
 
41
require_once($CFG->dirroot . '/mod/forum/lib.php');
42
 
43
/**
44
 * Capability manager for the forum.
45
 *
46
 * Defines all the business rules for what a user can and can't do in the forum.
47
 *
48
 * @copyright  2019 Ryan Wyllie <ryan@moodle.com>
49
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
50
 */
51
class capability {
52
    /** @var legacy_forum_data_mapper $forumdatamapper Legacy forum data mapper */
53
    private $forumdatamapper;
54
    /** @var legacy_discussion_data_mapper $discussiondatamapper Legacy discussion data mapper */
55
    private $discussiondatamapper;
56
    /** @var legacy_post_data_mapper $postdatamapper Legacy post data mapper */
57
    private $postdatamapper;
58
    /** @var forum_entity $forum Forum entity */
59
    private $forum;
60
    /** @var stdClass $forumrecord Legacy forum record */
61
    private $forumrecord;
62
    /** @var context $context Module context for the forum */
63
    private $context;
64
    /** @var array $canviewpostcache Cache of discussion posts that can be viewed by a user. */
65
    protected $canviewpostcache = [];
66
 
67
    /**
68
     * Constructor.
69
     *
70
     * @param forum_entity $forum The forum entity to manage capabilities for.
71
     * @param legacy_forum_data_mapper $forumdatamapper Legacy forum data mapper
72
     * @param legacy_discussion_data_mapper $discussiondatamapper Legacy discussion data mapper
73
     * @param legacy_post_data_mapper $postdatamapper Legacy post data mapper
74
     */
75
    public function __construct(
76
        forum_entity $forum,
77
        legacy_forum_data_mapper $forumdatamapper,
78
        legacy_discussion_data_mapper $discussiondatamapper,
79
        legacy_post_data_mapper $postdatamapper
80
    ) {
81
        $this->forumdatamapper = $forumdatamapper;
82
        $this->discussiondatamapper = $discussiondatamapper;
83
        $this->postdatamapper = $postdatamapper;
84
        $this->forum = $forum;
85
        $this->forumrecord = $forumdatamapper->to_legacy_object($forum);
86
        $this->context = $forum->get_context();
87
    }
88
 
89
    /**
90
     * Can the user subscribe to this forum?
91
     *
92
     * @param stdClass $user The user to check
93
     * @return bool
94
     */
95
    public function can_subscribe_to_forum(stdClass $user): bool {
96
        if ($this->forum->get_type() == 'single') {
97
            return false;
98
        }
99
 
100
        return !is_guest($this->get_context(), $user) &&
101
            subscriptions::is_subscribable($this->get_forum_record());
102
    }
103
 
104
    /**
105
     * Can the user create discussions in this forum?
106
     *
107
     * @param stdClass $user The user to check
108
     * @param int|null $groupid The current activity group id
109
     * @return bool
110
     */
111
    public function can_create_discussions(stdClass $user, int $groupid = null): bool {
112
        if (isguestuser($user) or !isloggedin()) {
113
            return false;
114
        }
115
 
116
        if ($this->forum->is_cutoff_date_reached()) {
117
            if (!has_capability('mod/forum:canoverridecutoff', $this->get_context())) {
118
                return false;
119
            }
120
        }
121
 
122
        // If the user reaches the number of posts equal to warning/blocking setting then return the value of canpost in $warningobj.
123
        $cmrecord = $this->forum->get_course_module_record();
124
        if ($warningobj = forum_check_throttling($this->forumrecord, $cmrecord)) {
125
            return $warningobj->canpost;
126
        }
127
 
128
        switch ($this->forum->get_type()) {
129
            case 'news':
130
                $capability = 'mod/forum:addnews';
131
                break;
132
            case 'qanda':
133
                $capability = 'mod/forum:addquestion';
134
                break;
135
            default:
136
                $capability = 'mod/forum:startdiscussion';
137
        }
138
 
139
        if (!has_capability($capability, $this->forum->get_context(), $user)) {
140
            return false;
141
        }
142
 
143
        if ($this->forum->get_type() == 'eachuser') {
144
            if (forum_user_has_posted_discussion($this->forum->get_id(), $user->id, $groupid)) {
145
                return false;
146
            }
147
        }
148
 
149
        if ($this->forum->is_in_group_mode()) {
150
            return $groupid ? $this->can_access_group($user, $groupid) : $this->can_access_all_groups($user);
151
        } else {
152
            return true;
153
        }
154
    }
155
 
156
    /**
157
     * Can the user access all groups?
158
     *
159
     * @param stdClass $user The user to check
160
     * @return bool
161
     */
162
    public function can_access_all_groups(stdClass $user): bool {
163
        return has_capability('moodle/site:accessallgroups', $this->get_context(), $user);
164
    }
165
 
166
    /**
167
     * Can the user access the given group?
168
     *
169
     * @param stdClass $user The user to check
170
     * @param int $groupid The id of the group that the forum is set to
171
     * @return bool
172
     */
173
    public function can_access_group(stdClass $user, int $groupid): bool {
174
        if ($this->can_access_all_groups($user)) {
175
            // This user has access to all groups.
176
            return true;
177
        }
178
 
179
        // This is a group discussion for a forum in separate groups mode.
180
        // Check if the user is a member.
181
        // This is the most expensive check.
182
        return groups_is_member($groupid, $user->id);
183
    }
184
 
185
    /**
186
     * Can the user post to their groups?
187
     *
188
     * @param stdClass $user The user to check
189
     * @return bool
190
     */
191
    public function can_post_to_my_groups(stdClass $user): bool {
192
        return has_capability('mod/forum:canposttomygroups', $this->get_context(), $user);
193
    }
194
 
195
    /**
196
     * Can the user view discussions in this forum?
197
     *
198
     * @param stdClass $user The user to check
199
     * @return bool
200
     */
201
    public function can_view_discussions(stdClass $user): bool {
202
        return has_capability('mod/forum:viewdiscussion', $this->get_context(), $user);
203
    }
204
 
205
    /**
206
     * Can the user move discussions in this forum?
207
     *
208
     * @param stdClass $user The user to check
209
     * @return bool
210
     */
211
    public function can_move_discussions(stdClass $user): bool {
212
        $forum = $this->get_forum();
213
        return $forum->get_type() !== 'single' &&
214
                has_capability('mod/forum:movediscussions', $this->get_context(), $user);
215
    }
216
 
217
    /**
218
     * Can the user pin discussions in this forum?
219
     *
220
     * @param stdClass $user The user to check
221
     * @return bool
222
     */
223
    public function can_pin_discussions(stdClass $user): bool {
224
        return $this->forum->get_type() !== 'single' &&
225
                has_capability('mod/forum:pindiscussions', $this->get_context(), $user);
226
    }
227
 
228
    /**
229
     * Can the user split discussions in this forum?
230
     *
231
     * @param stdClass $user The user to check
232
     * @return bool
233
     */
234
    public function can_split_discussions(stdClass $user): bool {
235
        $forum = $this->get_forum();
236
        return $forum->get_type() !== 'single' && has_capability('mod/forum:splitdiscussions', $this->get_context(), $user);
237
    }
238
 
239
    /**
240
     * Can the user export (see portfolios) discussions in this forum?
241
     *
242
     * @param stdClass $user The user to check
243
     * @return bool
244
     */
245
    public function can_export_discussions(stdClass $user): bool {
246
        global $CFG;
247
        return $CFG->enableportfolios && has_capability('mod/forum:exportdiscussion', $this->get_context(), $user);
248
    }
249
 
250
    /**
251
     * Can the user manually mark posts as read/unread in this forum?
252
     *
253
     * @param stdClass $user The user to check
254
     * @return bool
255
     */
256
    public function can_manually_control_post_read_status(stdClass $user): bool {
257
        global $CFG;
258
        return $CFG->forum_usermarksread && isloggedin() && forum_tp_is_tracked($this->get_forum_record(), $user);
259
    }
260
 
261
    /**
262
     * Is the user required to post in the discussion before they can view it?
263
     *
264
     * @param stdClass $user The user to check
265
     * @param discussion_entity $discussion The discussion to check
266
     * @return bool
267
     */
268
    public function must_post_before_viewing_discussion(stdClass $user, discussion_entity $discussion): bool {
269
        $forum = $this->get_forum();
270
 
271
        if ($forum->get_type() === 'qanda') {
272
            // If it's a Q and A forum then the user must either have the capability to view without
273
            // posting or the user must have posted before they can view the discussion.
274
            return !has_capability('mod/forum:viewqandawithoutposting', $this->get_context(), $user) &&
275
                !forum_user_has_posted($forum->get_id(), $discussion->get_id(), $user->id);
276
        } else {
277
            // No other forum types require posting before viewing.
278
            return false;
279
        }
280
    }
281
 
282
    /**
283
     * Can the user subscribe to the give discussion?
284
     *
285
     * @param stdClass $user The user to check
286
     * @param discussion_entity $discussion The discussion to check
287
     * @return bool
288
     */
289
    public function can_subscribe_to_discussion(stdClass $user, discussion_entity $discussion): bool {
290
        return $this->can_subscribe_to_forum($user);
291
    }
292
 
293
    /**
294
     * Can the user move the discussion in this forum?
295
     *
296
     * @param stdClass $user The user to check
297
     * @param discussion_entity $discussion The discussion to check
298
     * @return bool
299
     */
300
    public function can_move_discussion(stdClass $user, discussion_entity $discussion): bool {
301
        return $this->can_move_discussions($user);
302
    }
303
 
304
    /**
305
     * Is the user pin the discussion?
306
     *
307
     * @param stdClass $user The user to check
308
     * @param discussion_entity $discussion The discussion to check
309
     * @return bool
310
     */
311
    public function can_pin_discussion(stdClass $user, discussion_entity $discussion): bool {
312
        return $this->can_pin_discussions($user);
313
    }
314
 
315
    /**
316
     * Can the user post in this discussion?
317
     *
318
     * @param stdClass $user The user to check
319
     * @param discussion_entity $discussion The discussion to check
320
     * @return bool
321
     */
322
    public function can_post_in_discussion(stdClass $user, discussion_entity $discussion): bool {
323
        $forum = $this->get_forum();
324
        $forumrecord = $this->get_forum_record();
325
 
326
        $discussionrecord = $this->get_discussion_record($discussion);
327
        $context = $this->get_context();
328
        $coursemodule = $forum->get_course_module_record();
329
        $course = $forum->get_course_record();
330
 
331
        $status = forum_user_can_post($forumrecord, $discussionrecord, $user, $coursemodule, $course, $context);
332
 
333
        // If the user reaches the number of posts equal to warning/blocking setting then logically and canpost value with $status.
334
        if ($warningobj = forum_check_throttling($forumrecord, $coursemodule)) {
335
            return $status && $warningobj->canpost;
336
        }
337
        return $status;
338
    }
339
 
340
    /**
341
     * Can the user favourite the discussion
342
     *
343
     * @param stdClass $user The user to check
344
     * @return bool
345
     */
346
    public function can_favourite_discussion(stdClass $user): bool {
347
        $context = $this->get_context();
348
        return has_capability('mod/forum:cantogglefavourite', $context, $user);
349
    }
350
 
351
    /**
352
     * Can the user view the content of a discussion?
353
     *
354
     * @param stdClass $user The user to check
355
     * @param discussion_entity $discussion The discussion to check
356
     * @return bool
357
     */
358
    public function can_view_discussion(stdClass $user, discussion_entity $discussion): bool {
359
        $forumrecord = $this->get_forum_record();
360
        $discussionrecord = $this->get_discussion_record($discussion);
361
        $context = $this->get_context();
362
 
363
        return forum_user_can_see_discussion($forumrecord, $discussionrecord, $context, $user);
364
    }
365
 
366
    /**
367
     * Can the user view the content of the post in this discussion?
368
     *
369
     * @param stdClass $user The user to check
370
     * @param discussion_entity $discussion The discussion to check
371
     * @param post_entity $post The post the user wants to view
372
     * @return bool
373
     */
374
    public function can_view_post(stdClass $user, discussion_entity $discussion, post_entity $post): bool {
375
        if (!$this->can_view_post_shell($user, $post)) {
376
            return false;
377
        }
378
 
379
        // Return cached can view if possible.
380
        if (isset($this->canviewpostcache[$user->id][$post->get_id()])) {
381
            return $this->canviewpostcache[$user->id][$post->get_id()];
382
        }
383
 
384
        // Otherwise, check if the user can see this post.
385
        $forum = $this->get_forum();
386
        $forumrecord = $this->get_forum_record();
387
        $discussionrecord = $this->get_discussion_record($discussion);
388
        $postrecord = $this->get_post_record($post);
389
        $coursemodule = $forum->get_course_module_record();
390
        $canviewpost = forum_user_can_see_post($forumrecord, $discussionrecord, $postrecord, $user, $coursemodule, false);
391
 
392
        // Then cache the result before returning.
393
        $this->canviewpostcache[$user->id][$post->get_id()] = $canviewpost;
394
 
395
        return $canviewpost;
396
    }
397
 
398
    /**
399
     * Can the user view the post at all?
400
     * In some situations the user can view the shell of a post without being able to view its content.
401
     *
402
     * @param   stdClass $user The user to check
403
     * @param   post_entity $post The post the user wants to view
404
     * @return  bool
405
     *
406
     */
407
    public function can_view_post_shell(stdClass $user, post_entity $post): bool {
408
        if ($post->is_owned_by_user($user)) {
409
            return true;
410
        }
411
 
412
        if (!$post->is_private_reply()) {
413
            return true;
414
        }
415
 
416
        if ($post->is_private_reply_intended_for_user($user)) {
417
            return true;
418
        }
419
 
420
        return $this->can_view_any_private_reply($user);
421
    }
422
 
423
    /**
424
     * Whether the user can view any private reply in the forum.
425
     *
426
     * @param   stdClass $user The user to check
427
     * @return  bool
428
     */
429
    public function can_view_any_private_reply(stdClass $user): bool {
430
        return has_capability('mod/forum:readprivatereplies', $this->get_context(), $user);
431
    }
432
 
433
    /**
434
     * Can the user edit the post in this discussion?
435
     *
436
     * @param stdClass $user The user to check
437
     * @param discussion_entity $discussion The discussion to check
438
     * @param post_entity $post The post the user wants to edit
439
     * @return bool
440
     */
441
    public function can_edit_post(stdClass $user, discussion_entity $discussion, post_entity $post): bool {
442
        global $CFG;
443
 
444
        $context = $this->get_context();
445
        $ownpost = $post->is_owned_by_user($user);
446
        $ineditingtime = $post->get_age() < $CFG->maxeditingtime;
447
        $mailnow = $post->should_mail_now();
448
 
449
        switch ($this->forum->get_type()) {
450
            case 'news':
451
                // Allow editing of news posts once the discussion has started.
452
                $ineditingtime = !$post->has_parent() && $discussion->has_started();
453
                break;
454
            case 'single':
455
                if ($discussion->is_first_post($post)) {
456
                    return has_capability('moodle/course:manageactivities', $context, $user);
457
                }
458
                break;
459
        }
460
 
461
        return ($ownpost && $ineditingtime && !$mailnow) || has_capability('mod/forum:editanypost', $context, $user);
462
    }
463
 
464
    /**
465
     * Verifies is the given user can delete a post.
466
     *
467
     * @param stdClass $user The user to check
468
     * @param discussion_entity $discussion The discussion to check
469
     * @param post_entity $post The post the user wants to delete
470
     * @param bool $hasreplies Whether the post has replies
471
     * @return bool
472
     * @throws moodle_exception
473
     */
474
    public function validate_delete_post(stdClass $user, discussion_entity $discussion, post_entity $post,
475
            bool $hasreplies = false): void {
476
        global $CFG;
477
 
478
        $forum = $this->get_forum();
479
 
480
        if ($forum->get_type() == 'single' && $discussion->is_first_post($post)) {
481
            // Do not allow deleting of first post in single simple type.
482
            throw new moodle_exception('cannotdeletepost', 'forum');
483
        }
484
 
485
        $context = $this->get_context();
486
        $ownpost = $post->is_owned_by_user($user);
487
        $ineditingtime = $post->get_age() < $CFG->maxeditingtime;
488
        $mailnow = $post->should_mail_now();
489
 
490
        if (!($ownpost && $ineditingtime && has_capability('mod/forum:deleteownpost', $context, $user) && !$mailnow ||
491
                has_capability('mod/forum:deleteanypost', $context, $user))) {
492
 
493
            throw new moodle_exception('cannotdeletepost', 'forum');
494
        }
495
 
496
        if ($post->get_total_score()) {
497
            throw new moodle_exception('couldnotdeleteratings', 'rating');
498
        }
499
 
500
        if ($hasreplies && !has_capability('mod/forum:deleteanypost', $context, $user)) {
501
            throw new moodle_exception('couldnotdeletereplies', 'forum');
502
        }
503
    }
504
 
505
 
506
    /**
507
     * Can the user delete the post in this discussion?
508
     *
509
     * @param stdClass $user The user to check
510
     * @param discussion_entity $discussion The discussion to check
511
     * @param post_entity $post The post the user wants to delete
512
     * @param bool $hasreplies Whether the post has replies
513
     * @return bool
514
     */
515
    public function can_delete_post(stdClass $user, discussion_entity $discussion, post_entity $post,
516
            bool $hasreplies = false): bool {
517
 
518
        try {
519
            $this->validate_delete_post($user, $discussion, $post, $hasreplies);
520
            return true;
521
        } catch (moodle_exception $e) {
522
            return false;
523
        }
524
    }
525
 
526
    /**
527
     * Can the user split the post in this discussion?
528
     *
529
     * @param stdClass $user The user to check
530
     * @param discussion_entity $discussion The discussion to check
531
     * @param post_entity $post The post the user wants to split
532
     * @return bool
533
     */
534
    public function can_split_post(stdClass $user, discussion_entity $discussion, post_entity $post): bool {
535
        if ($post->is_private_reply()) {
536
            // It is not possible to create a private discussion.
537
            return false;
538
        }
539
 
540
        return $this->can_split_discussions($user) && $post->has_parent();
541
    }
542
 
543
    /**
544
     * Can the user reply to the post in this discussion?
545
     *
546
     * @param stdClass $user The user to check
547
     * @param discussion_entity $discussion The discussion to check
548
     * @param post_entity $post The post the user wants to reply to
549
     * @return bool
550
     */
551
    public function can_reply_to_post(stdClass $user, discussion_entity $discussion, post_entity $post): bool {
552
        if ($post->is_private_reply()) {
553
            // It is not possible to reply to a private reply.
554
            return false;
555
        } else if (!$this->can_view_post($user, $discussion, $post)) {
556
            // If the user cannot view the post in the first place, the user should not be able to reply to the post.
557
            return false;
558
        }
559
 
560
        return $this->can_post_in_discussion($user, $discussion);
561
    }
562
 
563
    /**
564
     * Can the user reply privately to the specified post?
565
     *
566
     * @param stdClass $user The user to check
567
     * @param post_entity $post The post the user wants to reply to
568
     * @return bool
569
     */
570
    public function can_reply_privately_to_post(stdClass $user, post_entity $post): bool {
571
        if ($post->is_private_reply()) {
572
            // You cannot reply privately to a post which is, itself, a private reply.
573
            return false;
574
        }
575
 
576
        return has_capability('mod/forum:postprivatereply', $this->get_context(), $user);
577
    }
578
 
579
    /**
580
     * Can the user export (see portfolios) the post in this discussion?
581
     *
582
     * @param stdClass $user The user to check
583
     * @param post_entity $post The post the user wants to export
584
     * @return bool
585
     */
586
    public function can_export_post(stdClass $user, post_entity $post): bool {
587
        global $CFG;
588
        $context = $this->get_context();
589
        return $CFG->enableportfolios  && (has_capability('mod/forum:exportpost', $context, $user) ||
590
            ($post->is_owned_by_user($user) && has_capability('mod/forum:exportownpost', $context, $user)));
591
    }
592
 
593
    /**
594
     * Get the forum entity for this capability manager.
595
     *
596
     * @return forum_entity
597
     */
598
    protected function get_forum(): forum_entity {
599
        return $this->forum;
600
    }
601
 
602
    /**
603
     * Get the legacy forum record for this forum.
604
     *
605
     * @return stdClass
606
     */
607
    protected function get_forum_record(): stdClass {
608
        return $this->forumrecord;
609
    }
610
 
611
    /**
612
     * Get the context for this capability manager.
613
     *
614
     * @return context
615
     */
616
    protected function get_context(): context {
617
        return $this->context;
618
    }
619
 
620
    /**
621
     * Get the legacy discussion record for the given discussion entity.
622
     *
623
     * @param discussion_entity $discussion The discussion to convert
624
     * @return stdClass
625
     */
626
    protected function get_discussion_record(discussion_entity $discussion): stdClass {
627
        return $this->discussiondatamapper->to_legacy_object($discussion);
628
    }
629
 
630
    /**
631
     * Get the legacy post record for the given post entity.
632
     *
633
     * @param post_entity $post The post to convert
634
     * @return stdClass
635
     */
636
    protected function get_post_record(post_entity $post): stdClass {
637
        return $this->postdatamapper->to_legacy_object($post);
638
    }
639
 
640
    /**
641
     * Can the user view the participants of this discussion?
642
     *
643
     * @param stdClass $user The user to check
644
     * @param discussion_entity $discussion The discussion to check
645
     * @return bool
646
     */
647
    public function can_view_participants(stdClass $user, discussion_entity $discussion): bool {
648
        return course_can_view_participants($this->get_context()) &&
649
            !$this->must_post_before_viewing_discussion($user, $discussion);
650
    }
651
 
652
    /**
653
     * Can the user view hidden posts in this forum?
654
     *
655
     * @param stdClass $user The user to check
656
     * @return bool
657
     */
658
    public function can_view_hidden_posts(stdClass $user): bool {
659
        return has_capability('mod/forum:viewhiddentimedposts', $this->get_context(), $user);
660
    }
661
 
662
    /**
663
     * Can the user manage this forum?
664
     *
665
     * @param stdClass $user The user to check
666
     * @return bool
667
     */
668
    public function can_manage_forum(stdClass $user) {
669
        return has_capability('moodle/course:manageactivities', $this->get_context(), $user);
670
    }
671
 
672
    /**
673
     * Can the user manage tags on the site?
674
     *
675
     * @param stdClass $user The user to check
676
     * @return bool
677
     */
678
    public function can_manage_tags(stdClass $user): bool {
679
        return has_capability('moodle/tag:manage', context_system::instance(), $user);
680
    }
681
 
682
    /**
683
     * Checks whether the user can self enrol into the course.
684
     * Mimics the checks on the add button in deprecatedlib/forum_print_latest_discussions
685
     *
686
     * @param stdClass $user
687
     * @return bool
688
     */
689
    public function can_self_enrol(stdClass $user): bool {
690
        $canstart = false;
691
 
692
        if ($this->forum->get_type() != 'news') {
693
            if (isguestuser($user) or !isloggedin()) {
694
                $canstart = true;
695
            }
696
 
697
            if (!is_enrolled($this->context) and !is_viewing($this->context)) {
698
                 // Allow guests and not-logged-in to see the button - they are prompted to log in after clicking the link,
699
                 // Normal users with temporary guest access see this button too, they are asked to enrol instead,
700
                 // Do not show the button to users with suspended enrolments here.
701
                $canstart = enrol_selfenrol_available($this->forum->get_course_id());
702
            }
703
        }
704
 
705
        return $canstart;
706
    }
707
 
708
    /**
709
     * Checks whether the user can export the whole forum (discussions and posts).
710
     *
711
     * @param stdClass $user The user object.
712
     * @return bool True if the user can export the forum or false otherwise.
713
     */
714
    public function can_export_forum(stdClass $user): bool {
715
        return has_capability('mod/forum:exportforum', $this->get_context(), $user);
716
    }
717
 
718
    /**
719
     * Check whether the supplied grader can grade the gradee.
720
     *
721
     * @param stdClass $grader The user grading
722
     * @param stdClass $gradee The user being graded
723
     * @return bool
724
     */
725
    public function can_grade(stdClass $grader, stdClass $gradee = null): bool {
726
        if (!has_capability('mod/forum:grade', $this->get_context(), $grader)) {
727
            return false;
728
        }
729
 
730
        return true;
731
    }
732
}