Merge branch 'MDL-31355-master' of git://github.com/rezaies/moodle

This commit is contained in:
Eloy Lafuente (stronk7)
2019-04-11 17:17:07 +02:00
24 changed files with 849 additions and 13 deletions
+1 -1
View File
@@ -1407,7 +1407,7 @@ function disable_output_buffering() {
*/
function is_major_upgrade_required() {
global $CFG;
$lastmajordbchanges = 2019032900.00;
$lastmajordbchanges = 2019041000.03;
$required = empty($CFG->version);
$required = $required || (float)$CFG->version < $lastmajordbchanges;
@@ -39,7 +39,7 @@ class backup_forum_activity_structure_step extends backup_activity_structure_ste
// Define each element separated
$forum = new backup_nested_element('forum', array('id'), array(
'type', 'name', 'intro', 'introformat',
'type', 'name', 'intro', 'introformat', 'duedate', 'cutoffdate',
'assessed', 'assesstimestart', 'assesstimefinish', 'scale',
'maxbytes', 'maxattachments', 'forcesubscribe', 'trackingtype',
'rsstype', 'rssarticles', 'timemodified', 'warnafter',
@@ -62,6 +62,14 @@ class restore_forum_activity_structure_step extends restore_activity_structure_s
// Any changes to the list of dates that needs to be rolled should be same during course restore and course reset.
// See MDL-9367.
if (!isset($data->duedate)) {
$data->duedate = 0;
}
$data->duedate = $this->apply_date_offset($data->duedate);
if (!isset($data->cutoffdate)) {
$data->cutoffdate = 0;
}
$data->cutoffdate = $this->apply_date_offset($data->cutoffdate);
$data->assesstimestart = $this->apply_date_offset($data->assesstimestart);
$data->assesstimefinish = $this->apply_date_offset($data->assesstimefinish);
if ($data->scale < 0) { // scale found, get mapping
@@ -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);
}
+73 -1
View File
@@ -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;
}
}
+3 -1
View File
@@ -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,23 @@ 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->is_due_date_reached()) {
$notifications[] = (new notification(
get_string('thisforumisdue', 'forum', userdate($forum->get_due_date())),
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,23 @@ 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->is_due_date_reached()) {
$notifications[] = (new notification(
get_string('thisforumisdue', 'forum', userdate($forum->get_due_date())),
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', [
+10
View File
@@ -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'
),
);
+3 -1
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="mod/forum/db" VERSION="20190311" COMMENT="XMLDB file for Moodle mod/forum"
<XMLDB PATH="mod/forum/db" VERSION="20190404" COMMENT="XMLDB file for Moodle mod/forum"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
>
@@ -12,6 +12,8 @@
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="intro" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="introformat" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="text format of intro field"/>
<FIELD NAME="duedate" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="A due date to show in the calendar. Not used for grading."/>
<FIELD NAME="cutoffdate" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The final date after which forum posts will no longer be accepted for this forum."/>
<FIELD NAME="assessed" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="assesstimestart" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="assesstimefinish" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+25 -1
View File
@@ -43,7 +43,7 @@
defined('MOODLE_INTERNAL') || die();
function xmldb_forum_upgrade($oldversion) {
global $CFG, $DB;
global $DB;
$dbman = $DB->get_manager(); // Loads ddl manager and xmldb classes.
@@ -118,5 +118,29 @@ function xmldb_forum_upgrade($oldversion) {
upgrade_mod_savepoint(true, 2019031200, 'forum');
}
if ($oldversion < 2019040400) {
$table = new xmldb_table('forum');
// Define field duedate to be added to forum.
$field = new xmldb_field('duedate', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'introformat');
// Conditionally launch add field duedate.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Define field cutoffdate to be added to forum.
$field = new xmldb_field('cutoffdate', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'duedate');
// Conditionally launch add field cutoffdate.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Forum savepoint reached.
upgrade_mod_savepoint(true, 2019040400, 'forum');
}
return true;
}
+2
View File
@@ -127,6 +127,8 @@ class mod_forum_external extends external_api {
'intro' => new external_value(PARAM_RAW, 'The forum intro'),
'introformat' => new external_format_value('intro'),
'introfiles' => new external_files('Files in the introduction text', VALUE_OPTIONAL),
'duedate' => new external_value(PARAM_INT, 'duedate for the user', VALUE_OPTIONAL),
'cutoffdate' => new external_value(PARAM_INT, 'cutoffdate for the user', VALUE_OPTIONAL),
'assessed' => new external_value(PARAM_INT, 'Aggregate type'),
'assesstimestart' => new external_value(PARAM_INT, 'Assess start time'),
'assesstimefinish' => new external_value(PARAM_INT, 'Assess finish time'),
+12
View File
@@ -43,6 +43,7 @@ $string['attachment_help'] = 'You can optionally attach one or more files to a f
$string['attachmentnopost'] = 'You cannot export attachments without a post id';
$string['attachments'] = 'Attachments';
$string['attachmentswordcount'] = 'Attachments and word count';
$string['availability'] = 'Availability';
$string['blockafter'] = 'Post threshold for blocking';
$string['blockafter_help'] = 'This setting specifies the maximum number of posts which a user can post in the given time period. Users with the capability mod/forum:postwithoutthrottling are exempt from post limits.';
$string['blockperiod'] = 'Time period for blocking';
@@ -51,6 +52,7 @@ $string['blockperioddisabled'] = 'Don\'t block';
$string['blogforum'] = 'Standard forum displayed in a blog-like format';
$string['bynameondate'] = 'by {$a->name} - {$a->date}';
$string['cachedef_forum_is_tracked'] = 'Forum tracking status for user';
$string['calendardue'] = '{$a} is due';
$string['cannotadd'] = 'Could not add the discussion for this forum';
$string['cannotadddiscussion'] = 'Adding discussions to this forum requires group membership.';
$string['cannotadddiscussionall'] = 'You do not have permission to add a new discussion topic for all participants.';
@@ -122,6 +124,10 @@ $string['couldnotadd'] = 'Could not add your post due to an unknown error';
$string['couldnotdeletereplies'] = 'Sorry, that cannot be deleted as people have already responded to it';
$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';
$string['deletedpost'] = 'The post has been deleted';
@@ -170,6 +176,9 @@ $string['displaystart'] = 'Display start';
$string['displaystart_help'] = 'This setting specifies whether a forum post should be displayed from a certain date. Note that administrators can always view forum posts.';
$string['displaywordcount'] = 'Display word count';
$string['displaywordcount_help'] = 'This setting specifies whether the word count of each post should be displayed or not.';
$string['duedate'] = 'Due date';
$string['duedate_help'] = 'This is when the forum is due. Although this date is displayed on the calendar as the due date for the forum, posting to the forum will still be allowed after this date. To prevent posting to the forum after a certain date - set the forum cut off date.';
$string['duedatetodisplayincalendar'] = 'Due date to display in calendar';
$string['eachuserforum'] = 'Each person posts one discussion';
$string['edit'] = 'Edit';
$string['editedby'] = 'Edited by {$a->name} - original submission {$a->date}';
@@ -227,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.';
@@ -579,6 +589,8 @@ $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['thisforumisdue'] = 'The due date for posting to this forum was {$a}.';
$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';
+154 -1
View File
@@ -86,6 +86,8 @@ define('FORUM_DISCUSSION_UNPINNED', 0);
function forum_add_instance($forum, $mform = null) {
global $CFG, $DB;
require_once($CFG->dirroot.'/mod/forum/locallib.php');
$forum->timemodified = time();
if (empty($forum->assessed)) {
@@ -127,6 +129,7 @@ function forum_add_instance($forum, $mform = null) {
}
}
forum_update_calendar($forum, $forum->coursemodule);
forum_grade_item_update($forum);
$completiontimeexpected = !empty($forum->completionexpected) ? $forum->completionexpected : null;
@@ -162,7 +165,9 @@ function forum_instance_created($context, $forum) {
* @return bool success
*/
function forum_update_instance($forum, $mform) {
global $DB, $OUTPUT, $USER;
global $CFG, $DB, $OUTPUT, $USER;
require_once($CFG->dirroot.'/mod/forum/locallib.php');
$forum->timemodified = time();
$forum->id = $forum->instance;
@@ -249,6 +254,7 @@ function forum_update_instance($forum, $mform) {
}
}
forum_update_calendar($forum, $forum->coursemodule);
forum_grade_item_update($forum);
$completiontimeexpected = !empty($forum->completionexpected) ? $forum->completionexpected : null;
@@ -3697,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);
}
@@ -3790,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)) {
@@ -6299,6 +6317,46 @@ 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 forum's due date is reached.
*
* @param stdClass $forum The forum
* @return bool
*/
function forum_is_due_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_due_date_reached();
}
/**
* Determine whether the specified discussion is time-locked.
*
@@ -6591,3 +6649,98 @@ function forum_user_can_reply_privately(\context_module $context, \stdClass $par
return has_capability('mod/forum:postprivatereply', $context);
}
/**
* This function calculates the minimum and maximum cutoff values for the timestart of
* the given event.
*
* It will return an array with two values, the first being the minimum cutoff value and
* the second being the maximum cutoff value. Either or both values can be null, which
* indicates there is no minimum or maximum, respectively.
*
* If a cutoff is required then the function must return an array containing the cutoff
* timestamp and error string to display to the user if the cutoff value is violated.
*
* A minimum and maximum cutoff return value will look like:
* [
* [1505704373, 'The date must be after this date'],
* [1506741172, 'The date must be before this date']
* ]
*
* @param calendar_event $event The calendar event to get the time range for
* @param stdClass $forum The module instance to get the range from
* @return array Returns an array with min and max date.
*/
function mod_forum_core_calendar_get_valid_event_timestart_range(\calendar_event $event, \stdClass $forum) {
global $CFG;
require_once($CFG->dirroot . '/mod/forum/locallib.php');
$mindate = null;
$maxdate = null;
if ($event->eventtype == FORUM_EVENT_TYPE_DUE) {
if (!empty($forum->cutoffdate)) {
$maxdate = [
$forum->cutoffdate,
get_string('cutoffdatevalidation', 'forum'),
];
}
}
return [$mindate, $maxdate];
}
/**
* This function will update the forum module according to the
* event that has been modified.
*
* It will set the timeclose value of the forum instance
* according to the type of event provided.
*
* @throws \moodle_exception
* @param \calendar_event $event
* @param stdClass $forum The module instance to get the range from
*/
function mod_forum_core_calendar_event_timestart_updated(\calendar_event $event, \stdClass $forum) {
global $CFG, $DB;
require_once($CFG->dirroot . '/mod/forum/locallib.php');
if ($event->eventtype != FORUM_EVENT_TYPE_DUE) {
return;
}
$courseid = $event->courseid;
$modulename = $event->modulename;
$instanceid = $event->instance;
// Something weird going on. The event is for a different module so
// we should ignore it.
if ($modulename != 'forum') {
return;
}
if ($forum->id != $instanceid) {
return;
}
$coursemodule = get_fast_modinfo($courseid)->instances[$modulename][$instanceid];
$context = context_module::instance($coursemodule->id);
// The user does not have the capability to modify this activity.
if (!has_capability('moodle/course:manageactivities', $context)) {
return;
}
if ($event->eventtype == FORUM_EVENT_TYPE_DUE) {
if ($forum->duedate != $event->timestart) {
$forum->duedate = $event->timestart;
$forum->timemodified = time();
// Persist the instance changes.
$DB->update_record('forum', $forum);
$event = \core\event\course_module_updated::create_from_cm($coursemodule, $context);
$event->trigger();
}
}
}
+54 -1
View File
@@ -1,5 +1,4 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
@@ -17,8 +16,15 @@
/**
* Library of functions for forum outside of the core api
*
* @package mod_forum
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// Event types.
define('FORUM_EVENT_TYPE_DUE', 'due');
require_once($CFG->dirroot . '/mod/forum/lib.php');
require_once($CFG->libdir . '/portfolio/caller.php');
@@ -700,3 +706,50 @@ function mod_forum_get_tagged_posts($tag, $exclusivemode = false, $fromctx = 0,
$exclusivemode, $fromctx, $ctx, $rec, $page, $totalpages);
}
}
/**
* Update the calendar entries for this forum activity.
*
* @param stdClass $forum the row from the database table forum.
* @param int $cmid The coursemodule id
* @return bool
*/
function forum_update_calendar($forum, $cmid) {
global $DB, $CFG;
require_once($CFG->dirroot.'/calendar/lib.php');
$event = new stdClass();
if (!empty($forum->duedate)) {
$event->name = get_string('calendardue', 'forum', $forum->name);
$event->description = format_module_intro('forum', $forum, $cmid);
$event->courseid = $forum->course;
$event->modulename = 'forum';
$event->instance = $forum->id;
$event->type = CALENDAR_EVENT_TYPE_ACTION;
$event->eventtype = FORUM_EVENT_TYPE_DUE;
$event->timestart = $forum->duedate;
$event->timesort = $forum->duedate;
$event->visible = instance_is_visible('forum', $forum);
}
$event->id = $DB->get_field('event', 'id',
array('modulename' => 'forum', 'instance' => $forum->id, 'eventtype' => FORUM_EVENT_TYPE_DUE));
if ($event->id) {
$calendarevent = calendar_event::load($event->id);
if (!empty($forum->duedate)) {
// Calendar event exists so update it.
$calendarevent->update($event);
} else {
// Calendar event is no longer needed.
$calendarevent->delete();
}
} else if (!empty($forum->duedate)) {
// Event doesn't exist so create one.
calendar_event::create($event);
}
return true;
}
+22
View File
@@ -54,6 +54,16 @@ class mod_forum_mod_form extends moodleform_mod {
$mform->addHelpButton('type', 'forumtype', 'forum');
$mform->setDefault('type', 'general');
$mform->addElement('header', 'availability', get_string('availability', 'forum'));
$name = get_string('duedate', 'forum');
$mform->addElement('date_time_selector', 'duedate', $name, array('optional' => true));
$mform->addHelpButton('duedate', 'duedate', 'forum');
$name = get_string('cutoffdate', 'forum');
$mform->addElement('date_time_selector', 'cutoffdate', $name, array('optional' => true));
$mform->addHelpButton('cutoffdate', 'cutoffdate', 'forum');
// Attachments and word count.
$mform->addElement('header', 'attachmentswordcounthdr', get_string('attachmentswordcount', 'forum'));
@@ -229,6 +239,18 @@ class mod_forum_mod_form extends moodleform_mod {
}
public function validation($data, $files) {
$errors = parent::validation($data, $files);
if ($data['duedate'] && $data['cutoffdate']) {
if ($data['duedate'] > $data['cutoffdate']) {
$errors['cutoffdate'] = get_string('cutoffdatevalidation', 'forum');
}
}
return $errors;
}
function data_preprocessing(&$default_values) {
parent::data_preprocessing($default_values);
+9 -1
View File
@@ -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());
}
}
+27
View File
@@ -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']);
}
/**
+325 -1
View File
@@ -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,100 @@ 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 the forum_is_due_date_reached function.
*
* @dataProvider forum_is_due_date_reached_provider
* @param stdClass $forum
* @param bool $expect
*/
public function test_forum_is_due_date_reached($forum, $expect) {
$this->resetAfterTest();
$this->setAdminUser();
$datagenerator = $this->getDataGenerator();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', (object) array_merge([
'course' => $course->id
], $forum));
$this->assertEquals($expect, forum_is_due_date_reached($forum));
}
/**
* Dataprovider for forum_is_due_date_reached tests.
*
* @return array
*/
public function forum_is_due_date_reached_provider() {
$now = time();
return [
'duedate is unset' => [
[],
false
],
'duedate is 0' => [
['duedate' => 0],
false
],
'duedate is set and is in future' => [
['duedate' => $now + 86400],
false
],
'duedate is set and is in past' => [
['duedate' => $now - 86400],
true
],
];
}
/**
* Test that {@link forum_update_post()} keeps correct forum_discussions usermodified.
*/
@@ -3639,4 +3767,200 @@ class mod_forum_lib_testcase extends advanced_testcase {
$this->setUser($otheruser->id);
$this->assertFalse(forum_post_is_visible_privately($post, $cm));
}
/**
* An unkown event type should not have any limits
*/
public function test_mod_forum_core_calendar_get_valid_event_timestart_range_unknown_event() {
global $CFG;
require_once($CFG->dirroot . "/calendar/lib.php");
$this->resetAfterTest(true);
$this->setAdminUser();
$generator = $this->getDataGenerator();
$course = $generator->create_course();
$duedate = time() + DAYSECS;
$forum = new \stdClass();
$forum->duedate = $duedate;
// Create a valid event.
$event = new \calendar_event([
'name' => 'Test event',
'description' => '',
'format' => 1,
'courseid' => $course->id,
'groupid' => 0,
'userid' => 2,
'modulename' => 'forum',
'instance' => 1,
'eventtype' => FORUM_EVENT_TYPE_DUE . "SOMETHING ELSE",
'timestart' => 1,
'timeduration' => 86400,
'visible' => 1
]);
list ($min, $max) = mod_forum_core_calendar_get_valid_event_timestart_range($event, $forum);
$this->assertNull($min);
$this->assertNull($max);
}
/**
* Forums configured without a cutoff date should not have any limits applied.
*/
public function test_mod_forum_core_calendar_get_valid_event_timestart_range_due_no_limit() {
global $CFG;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest(true);
$this->setAdminUser();
$generator = $this->getDataGenerator();
$course = $generator->create_course();
$duedate = time() + DAYSECS;
$forum = new \stdClass();
$forum->duedate = $duedate;
// Create a valid event.
$event = new \calendar_event([
'name' => 'Test event',
'description' => '',
'format' => 1,
'courseid' => $course->id,
'groupid' => 0,
'userid' => 2,
'modulename' => 'forum',
'instance' => 1,
'eventtype' => FORUM_EVENT_TYPE_DUE,
'timestart' => 1,
'timeduration' => 86400,
'visible' => 1
]);
list($min, $max) = mod_forum_core_calendar_get_valid_event_timestart_range($event, $forum);
$this->assertNull($min);
$this->assertNull($max);
}
/**
* Forums should be top bound by the cutoff date.
*/
public function test_mod_forum_core_calendar_get_valid_event_timestart_range_due_with_limits() {
global $CFG;
require_once($CFG->dirroot . '/calendar/lib.php');
$this->resetAfterTest(true);
$this->setAdminUser();
$generator = $this->getDataGenerator();
$course = $generator->create_course();
$duedate = time() + DAYSECS;
$cutoffdate = $duedate + DAYSECS;
$forum = new \stdClass();
$forum->duedate = $duedate;
$forum->cutoffdate = $cutoffdate;
// Create a valid event.
$event = new \calendar_event([
'name' => 'Test event',
'description' => '',
'format' => 1,
'courseid' => $course->id,
'groupid' => 0,
'userid' => 2,
'modulename' => 'forum',
'instance' => 1,
'eventtype' => FORUM_EVENT_TYPE_DUE,
'timestart' => 1,
'timeduration' => 86400,
'visible' => 1
]);
list($min, $max) = mod_forum_core_calendar_get_valid_event_timestart_range($event, $forum);
$this->assertNull($min);
$this->assertEquals($cutoffdate, $max[0]);
$this->assertNotEmpty($max[1]);
}
/**
* An unknown event type should not change the forum instance.
*/
public function test_mod_forum_core_calendar_event_timestart_updated_unknown_event() {
global $CFG, $DB;
require_once($CFG->dirroot . "/calendar/lib.php");
$this->resetAfterTest(true);
$this->setAdminUser();
$generator = $this->getDataGenerator();
$course = $generator->create_course();
$forumgenerator = $generator->get_plugin_generator('mod_forum');
$duedate = time() + DAYSECS;
$cutoffdate = $duedate + DAYSECS;
$forum = $forumgenerator->create_instance(['course' => $course->id]);
$forum->duedate = $duedate;
$forum->cutoffdate = $cutoffdate;
$DB->update_record('forum', $forum);
// Create a valid event.
$event = new \calendar_event([
'name' => 'Test event',
'description' => '',
'format' => 1,
'courseid' => $course->id,
'groupid' => 0,
'userid' => 2,
'modulename' => 'forum',
'instance' => $forum->id,
'eventtype' => FORUM_EVENT_TYPE_DUE . "SOMETHING ELSE",
'timestart' => 1,
'timeduration' => 86400,
'visible' => 1
]);
mod_forum_core_calendar_event_timestart_updated($event, $forum);
$forum = $DB->get_record('forum', ['id' => $forum->id]);
$this->assertEquals($duedate, $forum->duedate);
$this->assertEquals($cutoffdate, $forum->cutoffdate);
}
/**
* Due date events should update the forum due date.
*/
public function test_mod_forum_core_calendar_event_timestart_updated_due_event() {
global $CFG, $DB;
require_once($CFG->dirroot . "/calendar/lib.php");
$this->resetAfterTest(true);
$this->setAdminUser();
$generator = $this->getDataGenerator();
$course = $generator->create_course();
$forumgenerator = $generator->get_plugin_generator('mod_forum');
$duedate = time() + DAYSECS;
$cutoffdate = $duedate + DAYSECS;
$newduedate = $duedate + 1;
$forum = $forumgenerator->create_instance(['course' => $course->id]);
$forum->duedate = $duedate;
$forum->cutoffdate = $cutoffdate;
$DB->update_record('forum', $forum);
// Create a valid event.
$event = new \calendar_event([
'name' => 'Test event',
'description' => '',
'format' => 1,
'courseid' => $course->id,
'groupid' => 0,
'userid' => 2,
'modulename' => 'forum',
'instance' => $forum->id,
'eventtype' => FORUM_EVENT_TYPE_DUE,
'timestart' => $newduedate,
'timeduration' => 86400,
'visible' => 1
]);
mod_forum_core_calendar_event_timestart_updated($event, $forum);
$forum = $DB->get_record('forum', ['id' => $forum->id]);
$this->assertEquals($newduedate, $forum->duedate);
$this->assertEquals($cutoffdate, $forum->cutoffdate);
}
}
+73
View File
@@ -0,0 +1,73 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* File containing the forum module local library function tests.
*
* @package mod_forum
* @category test
* @copyright 2018 Shamim Rezaie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/mod/forum/lib.php');
/**
* Class mod_forum_locallib_testcase.
*
* @copyright 2018 Shamim Rezaie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_forum_locallib_testcase extends advanced_testcase {
public function test_forum_update_calendar() {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
// Create a course.
$course = $this->getDataGenerator()->create_course();
// Create a forum activity.
$time = time();
$forum = $this->getDataGenerator()->create_module('forum',
array(
'course' => $course->id,
'duedate' => $time
)
);
// Check that there is now an event in the database.
$events = $DB->get_records('event');
$this->assertCount(1, $events);
// Get the event.
$event = reset($events);
// Confirm the event is correct.
$this->assertEquals('forum', $event->modulename);
$this->assertEquals($forum->id, $event->instance);
$this->assertEquals(CALENDAR_EVENT_TYPE_ACTION, $event->type);
$this->assertEquals(FORUM_EVENT_TYPE_DUE, $event->eventtype);
$this->assertEquals($time, $event->timestart);
$this->assertEquals($time, $event->timesort);
}
}
+2
View File
@@ -9,6 +9,8 @@ information provided here is intended especially for developers.
* The get_forum_discussion_posts web service has been deprecated in favour of get_discussion_posts.
* The forum_count_replies function has been deprecated in favour of get_reply_count_for_post_id_in_discussion_id in
the Post vault.
* External function get_forums_by_courses now returns two additional fields "duedate" and "cutoffdate" containing the due date and the cutoff date
for posting to the forums respectively.
=== 3.6 ===
+1 -1
View File
@@ -24,6 +24,6 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2019031300; // The current module version (Date: YYYYMMDDXX)
$plugin->version = 2019040400; // The current module version (Date: YYYYMMDDXX)
$plugin->requires = 2018112800; // Requires this Moodle version
$plugin->component = 'mod_forum'; // Full name of the plugin (used for diagnostics)
+1 -1
View File
@@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2019041000.02; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2019041000.03; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.