mirror of
https://github.com/moodle/moodle.git
synced 2025-01-19 14:27:22 +01:00
MDL-31355 mod_forum: Prevent users from posting after cut-off date
This commit is contained in:
parent
99bcb31895
commit
cbf63d8efc
@ -69,7 +69,9 @@ class forum {
|
||||
'completionreplies' => $forum->get_completion_replies(),
|
||||
'completionposts' => $forum->get_completion_posts(),
|
||||
'displaywordcount' => $forum->should_display_word_count(),
|
||||
'lockdiscussionafter' => $forum->get_lock_discussions_after()
|
||||
'lockdiscussionafter' => $forum->get_lock_discussions_after(),
|
||||
'duedate' => $forum->get_due_date(),
|
||||
'cutoffdate' => $forum->get_cutoff_date()
|
||||
];
|
||||
}, $forums);
|
||||
}
|
||||
|
@ -99,6 +99,10 @@ class forum {
|
||||
private $displaywordcounts;
|
||||
/** @var bool $lockdiscussionafter Timestamp after which discussions should be locked */
|
||||
private $lockdiscussionafter;
|
||||
/** @var int $duedate Timestamp that represents the due date for forum posts */
|
||||
private $duedate;
|
||||
/** @var int $cutoffdate Timestamp after which forum posts will no longer be accepted */
|
||||
private $cutoffdate;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -132,6 +136,8 @@ class forum {
|
||||
* @param int $completionposts Completion posts
|
||||
* @param bool $displaywordcount Should display word counts in posts
|
||||
* @param int $lockdiscussionafter Timestamp after which discussions should be locked
|
||||
* @param int $duedate Timestamp that represents the due date for forum posts
|
||||
* @param int $cutoffdate Timestamp after which forum posts will no longer be accepted
|
||||
*/
|
||||
public function __construct(
|
||||
context $context,
|
||||
@ -162,7 +168,9 @@ class forum {
|
||||
int $completionreplies,
|
||||
int $completionposts,
|
||||
bool $displaywordcount,
|
||||
int $lockdiscussionafter
|
||||
int $lockdiscussionafter,
|
||||
int $duedate,
|
||||
int $cutoffdate
|
||||
) {
|
||||
$this->context = $context;
|
||||
$this->coursemodule = $coursemodule;
|
||||
@ -193,6 +201,8 @@ class forum {
|
||||
$this->completionposts = $completionposts;
|
||||
$this->displaywordcount = $displaywordcount;
|
||||
$this->lockdiscussionafter = $lockdiscussionafter;
|
||||
$this->duedate = $duedate;
|
||||
$this->cutoffdate = $cutoffdate;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -546,4 +556,66 @@ class forum {
|
||||
|
||||
return (($discussion->get_time_modified() + $this->get_lock_discussions_after()) < time());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cutoff date.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_cutoff_date() : int {
|
||||
return $this->cutoffdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the forum have a cutoff date?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_cutoff_date() : bool {
|
||||
return !empty($this->get_cutoff_date());
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the cutoff date for the forum reached?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_cutoff_date_reached() : bool {
|
||||
if ($this->has_cutoff_date() && ($this->get_cutoff_date() < time())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the due date.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_due_date() : int {
|
||||
return $this->duedate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the forum have a due date?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_due_date() : bool {
|
||||
return !empty($this->get_due_date());
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the due date for the forum reached?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_due_date_reached() : bool {
|
||||
if ($this->has_due_date() && ($this->get_due_date() < time())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +100,9 @@ class entity {
|
||||
$record->completionreplies,
|
||||
$record->completionposts,
|
||||
$record->displaywordcount,
|
||||
$record->lockdiscussionafter
|
||||
$record->lockdiscussionafter,
|
||||
$record->duedate,
|
||||
$record->cutoffdate
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -110,6 +110,12 @@ class capability {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->forum->is_cutoff_date_reached()) {
|
||||
if (!has_capability('mod/forum:canoverridecutoff', $this->get_context())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
switch ($this->forum->get_type()) {
|
||||
case 'news':
|
||||
$capability = 'mod/forum:addnews';
|
||||
|
@ -377,6 +377,18 @@ class discussion {
|
||||
$forum = $this->forum;
|
||||
$renderer = $this->renderer;
|
||||
|
||||
if ($forum->is_cutoff_date_reached()) {
|
||||
$notifications[] = (new notification(
|
||||
get_string('cutoffdatereached', 'forum'),
|
||||
notification::NOTIFY_INFO
|
||||
))->set_show_closebutton();
|
||||
} else if ($forum->has_due_date()) {
|
||||
$notifications[] = (new notification(
|
||||
get_string('thisforumhasduedate', 'forum', userdate($forum->get_due_date())),
|
||||
notification::NOTIFY_INFO
|
||||
))->set_show_closebutton();
|
||||
}
|
||||
|
||||
if ($forum->is_discussion_locked($discussion)) {
|
||||
$notifications[] = (new notification(
|
||||
get_string('discussionlocked', 'forum'),
|
||||
|
@ -327,6 +327,18 @@ class discussion_list {
|
||||
$renderer = $this->renderer;
|
||||
$capabilitymanager = $this->capabilitymanager;
|
||||
|
||||
if ($forum->is_cutoff_date_reached()) {
|
||||
$notifications[] = (new notification(
|
||||
get_string('cutoffdatereached', 'forum'),
|
||||
notification::NOTIFY_INFO
|
||||
))->set_show_closebutton();
|
||||
} else if ($forum->has_due_date()) {
|
||||
$notifications[] = (new notification(
|
||||
get_string('thisforumhasduedate', 'forum', userdate($forum->get_due_date())),
|
||||
notification::NOTIFY_INFO
|
||||
))->set_show_closebutton();
|
||||
}
|
||||
|
||||
if ($forum->has_blocking_enabled()) {
|
||||
$notifications[] = (new notification(
|
||||
get_string('thisforumisthrottled', 'forum', [
|
||||
|
@ -395,5 +395,15 @@ $capabilities = array(
|
||||
'manager' => CAP_ALLOW
|
||||
)
|
||||
),
|
||||
'mod/forum:canoverridecutoff' => array(
|
||||
'captype' => 'write',
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
'archetypes' => array(
|
||||
'teacher' => CAP_ALLOW,
|
||||
'editingteacher' => CAP_ALLOW,
|
||||
'manager' => CAP_ALLOW
|
||||
),
|
||||
'clonepermissionsfrom' => 'mod/forum:canoverridediscussionlock'
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -126,6 +126,7 @@ $string['couldnotupdate'] = 'Could not update your post due to an unknown error'
|
||||
$string['crontask'] = 'Forum mailings and maintenance jobs';
|
||||
$string['cutoffdate'] = 'Cut-off date';
|
||||
$string['cutoffdate_help'] = 'If set, the forum will not accept posts after this date.';
|
||||
$string['cutoffdatereached'] = 'The cut-off date for posting to this forum is reached so you can no longer post to it.';
|
||||
$string['cutoffdatevalidation'] = 'The cut-off date cannot be earlier than the due date.';
|
||||
$string['delete'] = 'Delete';
|
||||
$string['deleteddiscussion'] = 'The discussion topic has been deleted';
|
||||
@ -235,6 +236,7 @@ $string['forum:addinstance'] = 'Add a new forum';
|
||||
$string['forum:addnews'] = 'Add announcements';
|
||||
$string['forum:addquestion'] = 'Add question';
|
||||
$string['forum:allowforcesubscribe'] = 'Allow force subscribe';
|
||||
$string['forum:canoverridecutoff'] = 'Post to forums after their cut-off date';
|
||||
$string['forum:canoverridediscussionlock'] = 'Reply to locked discussions';
|
||||
$string['forumauthorhidden'] = 'Author (hidden)';
|
||||
$string['forumblockingalmosttoomanyposts'] = 'You are approaching the posting threshold. You have posted {$a->numposts} times in the last {$a->blockperiod} and the limit is {$a->blockafter} posts.';
|
||||
@ -587,6 +589,7 @@ $string['subscriptions'] = 'Subscriptions';
|
||||
$string['tagarea_forum_posts'] = 'Forum posts';
|
||||
$string['tagsdeleted'] = 'Forum tags have been deleted';
|
||||
$string['thisforumisthrottled'] = 'This forum has a limit to the number of forum postings you can make in a given time period - this is currently set at {$a->blockafter} posting(s) in {$a->blockperiod}';
|
||||
$string['thisforumhasduedate'] = 'The due date for posting to this forum is {$a}.';
|
||||
$string['timedhidden'] = 'Timed status: Hidden from students';
|
||||
$string['timedposts'] = 'Timed posts';
|
||||
$string['timedvisible'] = 'Timed status: Visible to all users';
|
||||
|
@ -3703,6 +3703,12 @@ function forum_user_can_post_discussion($forum, $currentgroup=null, $unused=-1,
|
||||
$context = context_module::instance($cm->id);
|
||||
}
|
||||
|
||||
if (forum_is_cutoff_date_reached($forum)) {
|
||||
if (!has_capability('mod/forum:canoverridecutoff', $context)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($currentgroup === null) {
|
||||
$currentgroup = groups_get_activity_group($cm);
|
||||
}
|
||||
@ -3796,6 +3802,12 @@ function forum_user_can_post($forum, $discussion, $user=NULL, $cm=NULL, $course=
|
||||
$context = context_module::instance($cm->id);
|
||||
}
|
||||
|
||||
if (forum_is_cutoff_date_reached($forum)) {
|
||||
if (!has_capability('mod/forum:canoverridecutoff', $context)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether the discussion is locked.
|
||||
if (forum_discussion_is_locked($forum, $discussion)) {
|
||||
if (!has_capability('mod/forum:canoverridediscussionlock', $context)) {
|
||||
@ -6305,6 +6317,26 @@ function mod_forum_inplace_editable($itemtype, $itemid, $newvalue) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the specified forum's cutoff date is reached.
|
||||
*
|
||||
* @param stdClass $forum The forum
|
||||
* @return bool
|
||||
*/
|
||||
function forum_is_cutoff_date_reached($forum) {
|
||||
$entityfactory = \mod_forum\local\container::get_entity_factory();
|
||||
$coursemoduleinfo = get_fast_modinfo($forum->course);
|
||||
$cminfo = $coursemoduleinfo->instances['forum'][$forum->id];
|
||||
$forumentity = $entityfactory->get_forum_from_stdclass(
|
||||
$forum,
|
||||
context_module::instance($cminfo->id),
|
||||
$cminfo->get_course_module_record(),
|
||||
$cminfo->get_course()
|
||||
);
|
||||
|
||||
return $forumentity->is_cutoff_date_reached();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the specified discussion is time-locked.
|
||||
*
|
||||
|
@ -92,6 +92,8 @@ class mod_forum_entities_forum_testcase extends advanced_testcase {
|
||||
$completionposts = 0;
|
||||
$displaywordcount = false;
|
||||
$lockdiscussionafter = 0;
|
||||
$duedate = 0;
|
||||
$cutoffdate = 0;
|
||||
|
||||
$forum = new forum_entity(
|
||||
$context,
|
||||
@ -122,7 +124,9 @@ class mod_forum_entities_forum_testcase extends advanced_testcase {
|
||||
$completionreplies,
|
||||
$completionposts,
|
||||
$displaywordcount,
|
||||
$lockdiscussionafter
|
||||
$lockdiscussionafter,
|
||||
$duedate,
|
||||
$cutoffdate
|
||||
);
|
||||
|
||||
$this->assertEquals($context, $forum->get_context());
|
||||
@ -160,5 +164,9 @@ class mod_forum_entities_forum_testcase extends advanced_testcase {
|
||||
$this->assertEquals($lockdiscussionafter, $forum->get_lock_discussions_after());
|
||||
$this->assertEquals(false, $forum->has_lock_discussions_after());
|
||||
$this->assertEquals(false, $forum->is_discussion_locked($discussion));
|
||||
$this->assertEquals(false, $forum->has_due_date());
|
||||
$this->assertEquals(false, $forum->is_due_date_reached());
|
||||
$this->assertEquals(false, $forum->has_cutoff_date());
|
||||
$this->assertEquals(false, $forum->is_cutoff_date_reached());
|
||||
}
|
||||
}
|
||||
|
@ -1458,7 +1458,34 @@ class mod_forum_external_testcase extends externallib_advanced_testcase {
|
||||
$this->assertTrue($result['status']);
|
||||
$this->assertTrue($result['canpindiscussions']);
|
||||
$this->assertTrue($result['cancreateattachment']);
|
||||
}
|
||||
|
||||
/*
|
||||
* A basic test to make sure users cannot post to forum after the cutoff date.
|
||||
*/
|
||||
public function test_can_add_discussion_after_cutoff() {
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
// Create courses to add the modules.
|
||||
$course = self::getDataGenerator()->create_course();
|
||||
|
||||
$user = self::getDataGenerator()->create_user();
|
||||
|
||||
// Create a forum with cutoff date set to a past date.
|
||||
$forum = self::getDataGenerator()->create_module('forum', ['course' => $course->id, 'cutoffdate' => time() - 1]);
|
||||
|
||||
// User with no mod/forum:canoverridecutoff capability.
|
||||
self::setUser($user);
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id);
|
||||
|
||||
$result = mod_forum_external::can_add_discussion($forum->id);
|
||||
$result = external_api::clean_returnvalue(mod_forum_external::can_add_discussion_returns(), $result);
|
||||
$this->assertFalse($result['status']);
|
||||
|
||||
self::setAdminUser();
|
||||
$result = mod_forum_external::can_add_discussion($forum->id);
|
||||
$result = external_api::clean_returnvalue(mod_forum_external::can_add_discussion_returns(), $result);
|
||||
$this->assertTrue($result['status']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2200,7 +2200,7 @@ class mod_forum_lib_testcase extends advanced_testcase {
|
||||
* Test forum_user_can_post_discussion
|
||||
*/
|
||||
public function test_forum_user_can_post_discussion() {
|
||||
global $CFG, $DB;
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
@ -2297,6 +2297,40 @@ class mod_forum_lib_testcase extends advanced_testcase {
|
||||
$this->assertTrue($can);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test forum_user_can_post_discussion_after_cutoff
|
||||
*/
|
||||
public function test_forum_user_can_post_discussion_after_cutoff() {
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
// Create course to add the module.
|
||||
$course = self::getDataGenerator()->create_course(array('groupmode' => SEPARATEGROUPS, 'groupmodeforce' => 1));
|
||||
$student = self::getDataGenerator()->create_user();
|
||||
$teacher = self::getDataGenerator()->create_user();
|
||||
$this->getDataGenerator()->enrol_user($student->id, $course->id);
|
||||
$this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher');
|
||||
|
||||
// Forum forcing separate gropus.
|
||||
$record = new stdClass();
|
||||
$record->course = $course->id;
|
||||
$record->cutoffdate = time() - 1;
|
||||
$forum = self::getDataGenerator()->create_module('forum', $record);
|
||||
$cm = get_coursemodule_from_instance('forum', $forum->id);
|
||||
$context = context_module::instance($cm->id);
|
||||
|
||||
self::setUser($student);
|
||||
|
||||
// Students usually don't have the mod/forum:canoverridecutoff capability.
|
||||
$can = forum_user_can_post_discussion($forum, null, -1, $cm, $context);
|
||||
$this->assertFalse($can);
|
||||
|
||||
self::setUser($teacher);
|
||||
|
||||
// Teachers usually have the mod/forum:canoverridecutoff capability.
|
||||
$can = forum_user_can_post_discussion($forum, null, -1, $cm, $context);
|
||||
$this->assertTrue($can);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test forum_user_has_posted_discussion with no groups.
|
||||
*/
|
||||
@ -3138,6 +3172,52 @@ class mod_forum_lib_testcase extends advanced_testcase {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the forum_is_cutoff_date_reached function.
|
||||
*
|
||||
* @dataProvider forum_is_cutoff_date_reached_provider
|
||||
* @param array $forum
|
||||
* @param bool $expect
|
||||
*/
|
||||
public function test_forum_is_cutoff_date_reached($forum, $expect) {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$datagenerator = $this->getDataGenerator();
|
||||
$course = $datagenerator->create_course();
|
||||
$forum = $datagenerator->create_module('forum', (object) array_merge([
|
||||
'course' => $course->id
|
||||
], $forum));
|
||||
|
||||
$this->assertEquals($expect, forum_is_cutoff_date_reached($forum));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dataprovider for forum_is_cutoff_date_reached tests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function forum_is_cutoff_date_reached_provider() {
|
||||
$now = time();
|
||||
return [
|
||||
'cutoffdate is unset' => [
|
||||
[],
|
||||
false
|
||||
],
|
||||
'cutoffdate is 0' => [
|
||||
['cutoffdate' => 0],
|
||||
false
|
||||
],
|
||||
'cutoffdate is set and is in future' => [
|
||||
['cutoffdate' => $now + 86400],
|
||||
false
|
||||
],
|
||||
'cutoffdate is set and is in past' => [
|
||||
['cutoffdate' => $now - 86400],
|
||||
true
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that {@link forum_update_post()} keeps correct forum_discussions usermodified.
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user