Merge branch 'MDL-71410-311' of git://github.com/rezaies/moodle into MOODLE_311_STABLE
This commit is contained in:
@@ -1121,7 +1121,9 @@ class completion_info {
|
||||
// Custom activity module completion data.
|
||||
|
||||
// Cast custom data to array before checking for custom completion rules.
|
||||
$customdata = (array)$cm->customdata;
|
||||
// We call ->get_custom_data() instead of ->customdata here because there is the chance of recursive calling,
|
||||
// and we cannot call a getter from a getter in PHP.
|
||||
$customdata = (array) $cm->get_custom_data();
|
||||
// Return early if the plugin does not define custom completion rules.
|
||||
if (empty($customdata['customcompletionrules'])) {
|
||||
return $data;
|
||||
|
||||
+24
-3
@@ -701,7 +701,7 @@ class course_modinfo {
|
||||
* {@link cached_cm_info}
|
||||
*
|
||||
* <b>Stage 2 - dynamic data.</b>
|
||||
* Dynamic data is user-dependend, it is stored in request-level cache. To reset this cache
|
||||
* Dynamic data is user-dependent, it is stored in request-level cache. To reset this cache
|
||||
* {@link get_fast_modinfo()} with $reset argument may be called.
|
||||
*
|
||||
* Dynamic data is obtained when any of the following properties/methods is requested:
|
||||
@@ -722,6 +722,7 @@ class course_modinfo {
|
||||
* - {@link cm_info::set_user_visible()}
|
||||
* - {@link cm_info::set_on_click()}
|
||||
* - {@link cm_info::set_icon_url()}
|
||||
* - {@link cm_info::override_customdata()}
|
||||
* Any methods affecting view elements can also be set in this callback.
|
||||
*
|
||||
* <b>Stage 3 (view data).</b>
|
||||
@@ -1419,9 +1420,15 @@ class cm_info implements IteratorAggregate {
|
||||
return $this->onclick;
|
||||
}
|
||||
/**
|
||||
* Getter method for property $customdata, ensures that dynamic data is retrieved.
|
||||
*
|
||||
* This method is normally called by the property ->customdata, but can be called directly if there
|
||||
* is a case when it might be called recursively (you can't call property values recursively).
|
||||
*
|
||||
* @return mixed Optional custom data stored in modinfo cache for this activity, or null if none
|
||||
*/
|
||||
private function get_custom_data() {
|
||||
public function get_custom_data() {
|
||||
$this->obtain_dynamic_data();
|
||||
return $this->customdata;
|
||||
}
|
||||
|
||||
@@ -1679,6 +1686,19 @@ class cm_info implements IteratorAggregate {
|
||||
$this->onclick = $onclick;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the value of an element in the customdata array.
|
||||
*
|
||||
* @param string $name The key in the customdata array
|
||||
* @param mixed $value The value
|
||||
*/
|
||||
public function override_customdata($name, $value) {
|
||||
if (!is_array($this->customdata)) {
|
||||
$this->customdata = [];
|
||||
}
|
||||
$this->customdata[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets HTML that displays after link on course view page.
|
||||
* @param string $afterlink HTML string (empty string if none)
|
||||
@@ -1881,7 +1901,8 @@ class cm_info implements IteratorAggregate {
|
||||
* the module or not.
|
||||
*
|
||||
* As part of this function, the module's _cm_info_dynamic function from its lib.php will
|
||||
* be called (if it exists).
|
||||
* be called (if it exists). Make sure that the functions that are called here do not use
|
||||
* any getter magic method from cm_info.
|
||||
* @return void
|
||||
*/
|
||||
private function obtain_dynamic_data() {
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Cache data source for the assign overrides.
|
||||
*
|
||||
* @package mod_assign
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mod_assign\cache;
|
||||
|
||||
use cache_definition;
|
||||
|
||||
/**
|
||||
* Class assign_overrides
|
||||
*
|
||||
* @package mod_assign
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class overrides implements \cache_data_source {
|
||||
|
||||
/** @var overrides the singleton instance of this class. */
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* Returns an instance of the data source class that the cache can use for loading data using the other methods
|
||||
* specified by this interface.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance_for_cache(cache_definition $definition): overrides {
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new overrides();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the data for the key provided ready formatted for caching.
|
||||
*
|
||||
* @param string|int $key The key to load.
|
||||
* @return mixed What ever data should be returned, or false if it can't be loaded.
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
public function load_for_cache($key) {
|
||||
global $DB;
|
||||
|
||||
[$assignid, $ug, $ugid] = explode('_', $key);
|
||||
$assignid = (int) $assignid;
|
||||
|
||||
switch ($ug) {
|
||||
case 'u':
|
||||
$userid = (int) $ugid;
|
||||
$override = $DB->get_record(
|
||||
'assign_overrides',
|
||||
['assignid' => $assignid, 'userid' => $userid],
|
||||
'duedate, cutoffdate, allowsubmissionsfromdate'
|
||||
);
|
||||
break;
|
||||
case 'g':
|
||||
$groupid = (int) $ugid;
|
||||
$override = $DB->get_record(
|
||||
'assign_overrides',
|
||||
['assignid' => $assignid, 'groupid' => $groupid],
|
||||
'sortorder, duedate, cutoffdate, allowsubmissionsfromdate'
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new \coding_exception('Invalid cache key');
|
||||
}
|
||||
|
||||
// Return null instead of false, because false will not be cached.
|
||||
return $override ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads several keys for the cache.
|
||||
*
|
||||
* @param array $keys An array of keys each of which will be string|int.
|
||||
* @return array An array of matching data items.
|
||||
*/
|
||||
public function load_many_for_cache(array $keys) {
|
||||
$results = [];
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$results[] = $this->load_for_cache($key);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Contains the class for fetching the important dates in mod_assign for a given module instance and a user.
|
||||
*
|
||||
* @package mod_assign
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mod_assign;
|
||||
|
||||
use core\activity_dates;
|
||||
|
||||
/**
|
||||
* Class for fetching the important dates in mod_assign for a given module instance and a user.
|
||||
*
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class dates extends activity_dates {
|
||||
|
||||
/**
|
||||
* Returns a list of important dates in mod_assign
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_dates(): array {
|
||||
$timeopen = $this->cm->customdata['allowsubmissionsfromdate'] ?? null;
|
||||
$timedue = $this->cm->customdata['duedate'] ?? null;
|
||||
$now = time();
|
||||
$dates = [];
|
||||
|
||||
if ($timeopen) {
|
||||
$openlabelid = $timeopen > $now ? 'activitydate:submissionsopen' : 'activitydate:submissionsopened';
|
||||
$dates[] = [
|
||||
'label' => get_string($openlabelid, 'mod_assign'),
|
||||
'timestamp' => (int) $timeopen,
|
||||
];
|
||||
}
|
||||
|
||||
if ($timedue) {
|
||||
$dates[] = [
|
||||
'label' => get_string('activitydate:submissionsdue', 'mod_assign'),
|
||||
'timestamp' => (int) $timedue,
|
||||
];
|
||||
}
|
||||
|
||||
return $dates;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Defined caches used internally by the plugin.
|
||||
*
|
||||
* @package mod_assign
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$definitions = [
|
||||
'overrides' => [
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'simplekeys' => true,
|
||||
'datasource' => '\mod_assign\cache\overrides',
|
||||
],
|
||||
];
|
||||
@@ -22,6 +22,9 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
$string['activitydate:submissionsdue'] = 'Submissions due:';
|
||||
$string['activitydate:submissionsopen'] = 'Submissions open:';
|
||||
$string['activitydate:submissionsopened'] = 'Submissions opened:';
|
||||
$string['activityoverview'] = 'You have assignments that need attention';
|
||||
$string['addsubmission'] = 'Add submission';
|
||||
$string['addsubmission_help'] = 'You have not made a submission yet.';
|
||||
@@ -112,6 +115,7 @@ $string['batchsetmarkingworkflowstateforusers'] = 'Set marking workflow state fo
|
||||
$string['blindmarking'] = 'Anonymous submissions';
|
||||
$string['blindmarkingenabledwarning'] = 'Anonymous submissions are enabled for this activity. Grades will not be added to the gradebook until student identities are revealed via the grading action menu.';
|
||||
$string['blindmarking_help'] = 'Anonymous submissions hide the identity of students from markers. Anonymous submission settings will be locked once a submission or grade has been made in relation to this assignment.';
|
||||
$string['cachedef_overrides'] = 'User and group override information';
|
||||
$string['calendardue'] = '{$a} is due';
|
||||
$string['calendargradingdue'] = '{$a} is due to be graded';
|
||||
$string['changeuser'] = 'Change user';
|
||||
|
||||
+76
-2
@@ -485,10 +485,11 @@ function assign_extend_settings_navigation(settings_navigation $settings, naviga
|
||||
* will know about (most noticeably, an icon).
|
||||
*/
|
||||
function assign_get_coursemodule_info($coursemodule) {
|
||||
global $CFG, $DB;
|
||||
global $DB;
|
||||
|
||||
$dbparams = array('id'=>$coursemodule->instance);
|
||||
$fields = 'id, name, alwaysshowdescription, allowsubmissionsfromdate, intro, introformat, completionsubmit';
|
||||
$fields = 'id, name, alwaysshowdescription, allowsubmissionsfromdate, intro, introformat, completionsubmit,
|
||||
duedate, cutoffdate, allowsubmissionsfromdate';
|
||||
if (! $assignment = $DB->get_record('assign', $dbparams, $fields)) {
|
||||
return false;
|
||||
}
|
||||
@@ -507,9 +508,82 @@ function assign_get_coursemodule_info($coursemodule) {
|
||||
$result->customdata['customcompletionrules']['completionsubmit'] = $assignment->completionsubmit;
|
||||
}
|
||||
|
||||
// Populate some other values that can be used in calendar or on dashboard.
|
||||
if ($assignment->duedate) {
|
||||
$result->customdata['duedate'] = $assignment->duedate;
|
||||
}
|
||||
if ($assignment->cutoffdate) {
|
||||
$result->customdata['cutoffdate'] = $assignment->cutoffdate;
|
||||
}
|
||||
if ($assignment->allowsubmissionsfromdate) {
|
||||
$result->customdata['allowsubmissionsfromdate'] = $assignment->allowsubmissionsfromdate;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets dynamic information about a course module
|
||||
*
|
||||
* This function is called from cm_info when displaying the module
|
||||
*
|
||||
* @param cm_info $cm
|
||||
*/
|
||||
function mod_assign_cm_info_dynamic(cm_info $cm) {
|
||||
global $USER;
|
||||
|
||||
$cache = cache::make('mod_assign', 'overrides');
|
||||
$override = $cache->get("{$cm->instance}_u_{$USER->id}");
|
||||
|
||||
if (!$override) {
|
||||
$override = (object) [
|
||||
'allowsubmissionsfromdate' => null,
|
||||
'duedate' => null,
|
||||
'cutoffdate' => null,
|
||||
];
|
||||
}
|
||||
|
||||
// No need to look for group overrides if there are user overrides for all allowsubmissionsfromdate, duedate and cutoffdate.
|
||||
if (is_null($override->allowsubmissionsfromdate) || is_null($override->duedate) || is_null($override->cutoffdate)) {
|
||||
$selectedgroupoverride = (object) [
|
||||
'allowsubmissionsfromdate' => null,
|
||||
'duedate' => null,
|
||||
'cutoffdate' => null,
|
||||
'sortorder' => PHP_INT_MAX, // So that every sortorder read from DB is less than this.
|
||||
];
|
||||
$groupings = groups_get_user_groups($cm->course, $USER->id);
|
||||
foreach ($groupings[0] as $groupid) {
|
||||
$groupoverride = $cache->get("{$cm->instance}_g_{$groupid}");
|
||||
if ($groupoverride) {
|
||||
if ($groupoverride->sortorder < $selectedgroupoverride->sortorder) {
|
||||
$selectedgroupoverride = $groupoverride;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If there is a user override for a setting, ignore the group override.
|
||||
if (is_null($override->allowsubmissionsfromdate)) {
|
||||
$override->allowsubmissionsfromdate = $selectedgroupoverride->allowsubmissionsfromdate;
|
||||
}
|
||||
if (is_null($override->duedate)) {
|
||||
$override->duedate = $selectedgroupoverride->duedate;
|
||||
}
|
||||
if (is_null($override->cutoffdate)) {
|
||||
$override->cutoffdate = $selectedgroupoverride->cutoffdate;
|
||||
}
|
||||
}
|
||||
|
||||
// Populate some other values that can be used in calendar or on dashboard.
|
||||
if (!is_null($override->allowsubmissionsfromdate)) {
|
||||
$cm->override_customdata('allowsubmissionsfromdate', $override->allowsubmissionsfromdate);
|
||||
}
|
||||
if (!is_null($override->duedate)) {
|
||||
$cm->override_customdata('duedate', $override->duedate);
|
||||
}
|
||||
if (!is_null($override->cutoffdate)) {
|
||||
$cm->override_customdata('cutoffdate', $override->cutoffdate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback which returns human-readable strings describing the active completion custom rules for the module instance.
|
||||
*
|
||||
|
||||
+30
-6
@@ -874,8 +874,10 @@ class assign {
|
||||
$conds = array('modulename' => 'assign', 'instance' => $this->get_instance()->id);
|
||||
if (isset($override->userid)) {
|
||||
$conds['userid'] = $override->userid;
|
||||
$cachekey = "{$cm->instance}_u_{$override->userid}";
|
||||
} else {
|
||||
$conds['groupid'] = $override->groupid;
|
||||
$cachekey = "{$cm->instance}_g_{$override->groupid}";
|
||||
}
|
||||
$events = $DB->get_records('event', $conds);
|
||||
foreach ($events as $event) {
|
||||
@@ -884,6 +886,7 @@ class assign {
|
||||
}
|
||||
|
||||
$DB->delete_records('assign_overrides', array('id' => $overrideid));
|
||||
cache::make('mod_assign', 'overrides')->delete($cachekey);
|
||||
|
||||
// Set the common parameters for one of the events we will be triggering.
|
||||
$params = array(
|
||||
@@ -1203,6 +1206,8 @@ class assign {
|
||||
}
|
||||
}
|
||||
|
||||
$purgeoverrides = false;
|
||||
|
||||
// Remove user overrides.
|
||||
if (!empty($data->reset_assign_user_overrides)) {
|
||||
$DB->delete_records_select('assign_overrides',
|
||||
@@ -1211,6 +1216,7 @@ class assign {
|
||||
'component' => $componentstr,
|
||||
'item' => get_string('useroverridesdeleted', 'assign'),
|
||||
'error' => false);
|
||||
$purgeoverrides = true;
|
||||
}
|
||||
// Remove group overrides.
|
||||
if (!empty($data->reset_assign_group_overrides)) {
|
||||
@@ -1220,6 +1226,7 @@ class assign {
|
||||
'component' => $componentstr,
|
||||
'item' => get_string('groupoverridesdeleted', 'assign'),
|
||||
'error' => false);
|
||||
$purgeoverrides = true;
|
||||
}
|
||||
|
||||
// Updating dates - shift may be negative too.
|
||||
@@ -1237,6 +1244,8 @@ class assign {
|
||||
WHERE assignid =? AND cutoffdate <> 0",
|
||||
array($data->timeshift, $this->get_instance()->id));
|
||||
|
||||
$purgeoverrides = true;
|
||||
|
||||
// Any changes to the list of dates that needs to be rolled should be same during course restore and course reset.
|
||||
// See MDL-9367.
|
||||
shift_course_mod_dates('assign',
|
||||
@@ -1248,6 +1257,10 @@ class assign {
|
||||
'error'=>false);
|
||||
}
|
||||
|
||||
if ($purgeoverrides) {
|
||||
cache::make('mod_assign', 'overrides')->purge();
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
@@ -9790,14 +9803,14 @@ function assign_process_group_deleted_in_course($courseid, $groupid = null) {
|
||||
if ($groupid) {
|
||||
$params['groupid'] = $groupid;
|
||||
// We just update the group that was deleted.
|
||||
$sql = "SELECT o.id, o.assignid
|
||||
$sql = "SELECT o.id, o.assignid, o.groupid
|
||||
FROM {assign_overrides} o
|
||||
JOIN {assign} assign ON assign.id = o.assignid
|
||||
WHERE assign.course = :courseid
|
||||
AND o.groupid = :groupid";
|
||||
} else {
|
||||
// No groupid, we update all orphaned group overrides for all assign in course.
|
||||
$sql = "SELECT o.id, o.assignid
|
||||
$sql = "SELECT o.id, o.assignid, o.groupid
|
||||
FROM {assign_overrides} o
|
||||
JOIN {assign} assign ON assign.id = o.assignid
|
||||
LEFT JOIN {groups} grp ON grp.id = o.groupid
|
||||
@@ -9805,11 +9818,15 @@ function assign_process_group_deleted_in_course($courseid, $groupid = null) {
|
||||
AND o.groupid IS NOT NULL
|
||||
AND grp.id IS NULL";
|
||||
}
|
||||
$records = $DB->get_records_sql_menu($sql, $params);
|
||||
$records = $DB->get_records_sql($sql, $params);
|
||||
if (!$records) {
|
||||
return; // Nothing to do.
|
||||
}
|
||||
$DB->delete_records_list('assign_overrides', 'id', array_keys($records));
|
||||
$cache = cache::make('mod_assign', 'overrides');
|
||||
foreach ($records as $record) {
|
||||
$cache->delete("{$record->assignid}_g_{$record->groupid}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -9824,7 +9841,7 @@ function move_group_override($id, $move, $assignid) {
|
||||
global $DB;
|
||||
|
||||
// Get the override object.
|
||||
if (!$override = $DB->get_record('assign_overrides', array('id' => $id), 'id, sortorder')) {
|
||||
if (!$override = $DB->get_record('assign_overrides', ['id' => $id], 'id, sortorder, groupid')) {
|
||||
return false;
|
||||
}
|
||||
// Count the number of group overrides.
|
||||
@@ -9840,8 +9857,8 @@ function move_group_override($id, $move, $assignid) {
|
||||
}
|
||||
|
||||
// Retrieve the override object that is currently residing in the new position.
|
||||
$params = array('sortorder' => $neworder, 'assignid' => $assignid);
|
||||
if ($swapoverride = $DB->get_record('assign_overrides', $params, 'id, sortorder')) {
|
||||
$params = ['sortorder' => $neworder, 'assignid' => $assignid];
|
||||
if ($swapoverride = $DB->get_record('assign_overrides', $params, 'id, sortorder, groupid')) {
|
||||
|
||||
// Swap the sortorders.
|
||||
$swapoverride->sortorder = $override->sortorder;
|
||||
@@ -9850,6 +9867,11 @@ function move_group_override($id, $move, $assignid) {
|
||||
// Update the override records.
|
||||
$DB->update_record('assign_overrides', $override);
|
||||
$DB->update_record('assign_overrides', $swapoverride);
|
||||
|
||||
// Delete cache for the 2 records we updated above.
|
||||
$cache = cache::make('mod_assign', 'overrides');
|
||||
$cache->delete("{$override->assignid}_g_{$override->groupid}");
|
||||
$cache->delete("{$swapoverride->assignid}_g_{$swapoverride->groupid}");
|
||||
}
|
||||
|
||||
reorder_group_overrides($assignid);
|
||||
@@ -9866,11 +9888,13 @@ function reorder_group_overrides($assignid) {
|
||||
|
||||
$i = 1;
|
||||
if ($overrides = $DB->get_records('assign_overrides', array('userid' => null, 'assignid' => $assignid), 'sortorder ASC')) {
|
||||
$cache = cache::make('mod_assign', 'overrides');
|
||||
foreach ($overrides as $override) {
|
||||
$f = new stdClass();
|
||||
$f->id = $override->id;
|
||||
$f->sortorder = $i++;
|
||||
$DB->update_record('assign_overrides', $f);
|
||||
$cache->delete("{$assignid}_g_{$override->groupid}");
|
||||
|
||||
// Update priorities of group overrides.
|
||||
$params = [
|
||||
|
||||
@@ -187,6 +187,8 @@ if ($mform->is_cancelled()) {
|
||||
if (!empty($override->id)) {
|
||||
$fromform->id = $override->id;
|
||||
$DB->update_record('assign_overrides', $fromform);
|
||||
$cachekey = $groupmode ? "{$fromform->assignid}_g_{$fromform->groupid}" : "{$fromform->assignid}_u_{$fromform->userid}";
|
||||
cache::make('mod_assign', 'overrides')->delete($cachekey);
|
||||
|
||||
// Determine which override updated event to fire.
|
||||
$params['objectid'] = $override->id;
|
||||
@@ -219,6 +221,8 @@ if ($mform->is_cancelled()) {
|
||||
$DB->update_record('assign_overrides', $fromform);
|
||||
reorder_group_overrides($assigninstance->id);
|
||||
}
|
||||
$cachekey = $groupmode ? "{$fromform->assignid}_g_{$fromform->groupid}" : "{$fromform->assignid}_u_{$fromform->userid}";
|
||||
cache::make('mod_assign', 'overrides')->delete($cachekey);
|
||||
|
||||
// Determine which override created event to fire.
|
||||
$params['objectid'] = $fromform->id;
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Contains unit tests for mod_assign\dates.
|
||||
*
|
||||
* @package mod_assign
|
||||
* @category test
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mod_assign;
|
||||
|
||||
use advanced_testcase;
|
||||
use cm_info;
|
||||
use core\activity_dates;
|
||||
|
||||
/**
|
||||
* Class for unit testing mod_assign\dates.
|
||||
*
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class dates_test extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Data provider for get_dates_for_module().
|
||||
* @return array[]
|
||||
*/
|
||||
public function get_dates_for_module_provider(): array {
|
||||
$now = time();
|
||||
$before = $now - DAYSECS;
|
||||
$earlier = $before - DAYSECS;
|
||||
$after = $now + DAYSECS;
|
||||
$later = $after + DAYSECS;
|
||||
|
||||
return [
|
||||
'without any dates' => [
|
||||
null, null, null, null, null, null, []
|
||||
],
|
||||
'only with opening time' => [
|
||||
$after, null, null, null, null, null, [
|
||||
['label' => get_string('activitydate:submissionsopen', 'mod_assign'), 'timestamp' => $after],
|
||||
]
|
||||
],
|
||||
'only with closing time' => [
|
||||
null, $after, null, null, null, null, [
|
||||
['label' => get_string('activitydate:submissionsdue', 'mod_assign'), 'timestamp' => $after],
|
||||
]
|
||||
],
|
||||
'with both times' => [
|
||||
$after, $later, null, null, null, null, [
|
||||
['label' => get_string('activitydate:submissionsopen', 'mod_assign'), 'timestamp' => $after],
|
||||
['label' => get_string('activitydate:submissionsdue', 'mod_assign'), 'timestamp' => $later],
|
||||
]
|
||||
],
|
||||
'between the dates' => [
|
||||
$before, $after, null, null, null, null, [
|
||||
['label' => get_string('activitydate:submissionsopened', 'mod_assign'), 'timestamp' => $before],
|
||||
['label' => get_string('activitydate:submissionsdue', 'mod_assign'), 'timestamp' => $after],
|
||||
]
|
||||
],
|
||||
'dates are past' => [
|
||||
$earlier, $before, null, null, null, null, [
|
||||
['label' => get_string('activitydate:submissionsopened', 'mod_assign'), 'timestamp' => $earlier],
|
||||
['label' => get_string('activitydate:submissionsdue', 'mod_assign'), 'timestamp' => $before],
|
||||
]
|
||||
],
|
||||
'with user override' => [
|
||||
$before, $after, $earlier, $later, null, null, [
|
||||
['label' => get_string('activitydate:submissionsopened', 'mod_assign'), 'timestamp' => $earlier],
|
||||
['label' => get_string('activitydate:submissionsdue', 'mod_assign'), 'timestamp' => $later],
|
||||
]
|
||||
],
|
||||
'with group override' => [
|
||||
$before, $after, null, null, $earlier, $later, [
|
||||
['label' => get_string('activitydate:submissionsopened', 'mod_assign'), 'timestamp' => $earlier],
|
||||
['label' => get_string('activitydate:submissionsdue', 'mod_assign'), 'timestamp' => $later],
|
||||
]
|
||||
],
|
||||
'with both user and group overrides' => [
|
||||
$before, $after, $earlier, $later, $earlier - DAYSECS, $later + DAYSECS, [
|
||||
['label' => get_string('activitydate:submissionsopened', 'mod_assign'), 'timestamp' => $earlier],
|
||||
['label' => get_string('activitydate:submissionsdue', 'mod_assign'), 'timestamp' => $later],
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for get_dates_for_module().
|
||||
*
|
||||
* @dataProvider get_dates_for_module_provider
|
||||
* @param int|null $from Time of opening submissions in the assignment.
|
||||
* @param int|null $due Assignment's due date.
|
||||
* @param int|null $userfrom The user override for opening submissions.
|
||||
* @param int|null $userdue The user override for due date.
|
||||
* @param int|null $groupfrom The group override for opening submissions.
|
||||
* @param int|null $groupdue The group override for due date.
|
||||
* @param array $expected The expected value of calling get_dates_for_module()
|
||||
*/
|
||||
public function test_get_dates_for_module(?int $from, ?int $due,
|
||||
?int $userfrom, ?int $userdue,
|
||||
?int $groupfrom, ?int $groupdue,
|
||||
array $expected) {
|
||||
|
||||
$this->resetAfterTest();
|
||||
$generator = $this->getDataGenerator();
|
||||
/** @var \mod_assign_generator $assigngenerator */
|
||||
$assigngenerator = $generator->get_plugin_generator('mod_assign');
|
||||
|
||||
$course = $generator->create_course();
|
||||
$user = $generator->create_user();
|
||||
$generator->enrol_user($user->id, $course->id);
|
||||
|
||||
$data = ['course' => $course->id];
|
||||
if ($from) {
|
||||
$data['allowsubmissionsfromdate'] = $from;
|
||||
}
|
||||
if ($due) {
|
||||
$data['duedate'] = $due;
|
||||
}
|
||||
$assign = $assigngenerator->create_instance($data);
|
||||
|
||||
if ($userfrom || $userdue || $groupfrom || $groupdue) {
|
||||
$generator->enrol_user($user->id, $course->id);
|
||||
$group = $generator->create_group(['courseid' => $course->id]);
|
||||
$generator->create_group_member(['groupid' => $group->id, 'userid' => $user->id]);
|
||||
|
||||
if ($userfrom || $userdue) {
|
||||
$assigngenerator->create_override([
|
||||
'assignid' => $assign->id,
|
||||
'userid' => $user->id,
|
||||
'allowsubmissionsfromdate' => $userfrom,
|
||||
'duedate' => $userdue,
|
||||
]);
|
||||
}
|
||||
|
||||
if ($groupfrom || $groupdue) {
|
||||
$assigngenerator->create_override([
|
||||
'assignid' => $assign->id,
|
||||
'groupid' => $group->id,
|
||||
'allowsubmissionsfromdate' => $groupfrom,
|
||||
'duedate' => $groupdue,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->setUser($user);
|
||||
|
||||
$cm = get_coursemodule_from_instance('assign', $assign->id);
|
||||
// Make sure we're using a cm_info object.
|
||||
$cm = cm_info::create($cm);
|
||||
|
||||
$dates = activity_dates::get_dates_for_module($cm, (int) $user->id);
|
||||
|
||||
$this->assertEquals($expected, $dates);
|
||||
}
|
||||
}
|
||||
@@ -59,4 +59,28 @@ class mod_assign_generator extends testing_module_generator {
|
||||
|
||||
return parent::create_instance($record, (array)$options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an assign override (either user or group).
|
||||
*
|
||||
* @param array $data must specify assignid, and one of userid or groupid.
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public function create_override(array $data): void {
|
||||
global $DB;
|
||||
|
||||
if (!isset($data['assignid'])) {
|
||||
throw new coding_exception('Must specify assignid when creating an assign override.');
|
||||
}
|
||||
|
||||
if (!isset($data['userid']) && !isset($data['groupid'])) {
|
||||
throw new coding_exception('Must specify one of userid or groupid when creating an assign override.');
|
||||
}
|
||||
|
||||
if (isset($data['userid']) && isset($data['groupid'])) {
|
||||
throw new coding_exception('Cannot specify both userid and groupid when creating an assign override.');
|
||||
}
|
||||
|
||||
$DB->insert_record('assign_overrides', (object) $data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,5 +25,5 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->component = 'mod_assign'; // Full name of the plugin (used for diagnostics).
|
||||
$plugin->version = 2020110900; // The current module version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2021042700; // The current module version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2020110300; // Requires this Moodle version.
|
||||
|
||||
+1
-1
@@ -439,7 +439,7 @@ function folder_get_coursemodule_info($cm) {
|
||||
* @param cm_info $cm
|
||||
*/
|
||||
function folder_cm_info_dynamic(cm_info $cm) {
|
||||
if ($cm->customdata) {
|
||||
if ($cm->get_custom_data()) {
|
||||
// the field 'customdata' is not empty IF AND ONLY IF we display contens inline
|
||||
$cm->set_no_view_link();
|
||||
}
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Cache data source for the lesson overrides.
|
||||
*
|
||||
* @package mod_lesson
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mod_lesson\cache;
|
||||
|
||||
use cache_definition;
|
||||
|
||||
/**
|
||||
* Class lesson_overrides
|
||||
*
|
||||
* @package mod_lesson
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class overrides implements \cache_data_source {
|
||||
|
||||
/** @var overrides the singleton instance of this class. */
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* Returns an instance of the data source class that the cache can use for loading data using the other methods
|
||||
* specified by this interface.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance_for_cache(cache_definition $definition): overrides {
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new overrides();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the data for the key provided ready formatted for caching.
|
||||
*
|
||||
* @param string|int $key The key to load.
|
||||
* @return mixed What ever data should be returned, or false if it can't be loaded.
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
public function load_for_cache($key) {
|
||||
global $DB;
|
||||
|
||||
[$lessonid, $ug, $ugid] = explode('_', $key);
|
||||
$lessonid = (int) $lessonid;
|
||||
|
||||
switch ($ug) {
|
||||
case 'u':
|
||||
$userid = (int) $ugid;
|
||||
$override = $DB->get_record(
|
||||
'lesson_overrides',
|
||||
['lessonid' => $lessonid, 'userid' => $userid],
|
||||
'available, deadline, timelimit, review, maxattempts, retake, password'
|
||||
);
|
||||
break;
|
||||
case 'g':
|
||||
$groupid = (int) $ugid;
|
||||
$override = $DB->get_record(
|
||||
'lesson_overrides',
|
||||
['lessonid' => $lessonid, 'groupid' => $groupid],
|
||||
'available, deadline, timelimit, review, maxattempts, retake, password'
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new \coding_exception('Invalid cache key');
|
||||
}
|
||||
|
||||
// Return null instead of false, because false will not be cached.
|
||||
return $override ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads several keys for the cache.
|
||||
*
|
||||
* @param array $keys An array of keys each of which will be string|int.
|
||||
* @return array An array of matching data items.
|
||||
*/
|
||||
public function load_many_for_cache(array $keys) {
|
||||
$results = [];
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$results[] = $this->load_for_cache($key);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Contains the class for fetching the important dates in mod_lesson for a given module instance and a user.
|
||||
*
|
||||
* @package mod_lesson
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mod_lesson;
|
||||
|
||||
use core\activity_dates;
|
||||
|
||||
/**
|
||||
* Class for fetching the important dates in mod_lesson for a given module instance and a user.
|
||||
*
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class dates extends activity_dates {
|
||||
|
||||
/**
|
||||
* Returns a list of important dates in mod_lesson
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_dates(): array {
|
||||
$timeopen = $this->cm->customdata['available'] ?? null;
|
||||
$timeclose = $this->cm->customdata['deadline'] ?? null;
|
||||
$now = time();
|
||||
$dates = [];
|
||||
|
||||
if ($timeopen) {
|
||||
$openlabelid = $timeopen > $now ? 'activitydate:opens' : 'activitydate:opened';
|
||||
$dates[] = [
|
||||
'label' => get_string($openlabelid, 'course'),
|
||||
'timestamp' => (int) $timeopen,
|
||||
];
|
||||
}
|
||||
|
||||
if ($timeclose) {
|
||||
$closelabelid = $timeclose > $now ? 'activitydate:closes' : 'activitydate:closed';
|
||||
$dates[] = [
|
||||
'label' => get_string($closelabelid, 'course'),
|
||||
'timestamp' => (int) $timeclose,
|
||||
];
|
||||
}
|
||||
|
||||
return $dates;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Defined caches used internally by the plugin.
|
||||
*
|
||||
* @package mod_lesson
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$definitions = [
|
||||
'overrides' => [
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'simplekeys' => true,
|
||||
'datasource' => '\mod_lesson\cache\overrides',
|
||||
],
|
||||
];
|
||||
@@ -83,6 +83,7 @@ $string['averagescore'] = 'Average score';
|
||||
$string['averagetime'] = 'Average time';
|
||||
$string['branch'] = 'Content';
|
||||
$string['branchtable'] = 'Content';
|
||||
$string['cachedef_overrides'] = 'User and group override information';
|
||||
$string['cancel'] = 'Cancel';
|
||||
$string['cannotfindanswer'] = 'Error: could not find answer';
|
||||
$string['cannotfindattempt'] = 'Error: could not find attempt';
|
||||
|
||||
+81
-7
@@ -925,23 +925,27 @@ function lesson_reset_userdata($data) {
|
||||
$status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallattempts', 'lesson'), 'error'=>false);
|
||||
}
|
||||
|
||||
$purgeoverrides = false;
|
||||
|
||||
// Remove user overrides.
|
||||
if (!empty($data->reset_lesson_user_overrides)) {
|
||||
$DB->delete_records_select('lesson_overrides',
|
||||
'lessonid IN (SELECT id FROM {lesson} WHERE course = ?) AND userid IS NOT NULL', array($data->courseid));
|
||||
$status[] = array(
|
||||
'component' => $componentstr,
|
||||
'item' => get_string('useroverridesdeleted', 'lesson'),
|
||||
'error' => false);
|
||||
'component' => $componentstr,
|
||||
'item' => get_string('useroverridesdeleted', 'lesson'),
|
||||
'error' => false);
|
||||
$purgeoverrides = true;
|
||||
}
|
||||
// Remove group overrides.
|
||||
if (!empty($data->reset_lesson_group_overrides)) {
|
||||
$DB->delete_records_select('lesson_overrides',
|
||||
'lessonid IN (SELECT id FROM {lesson} WHERE course = ?) AND groupid IS NOT NULL', array($data->courseid));
|
||||
$status[] = array(
|
||||
'component' => $componentstr,
|
||||
'item' => get_string('groupoverridesdeleted', 'lesson'),
|
||||
'error' => false);
|
||||
'component' => $componentstr,
|
||||
'item' => get_string('groupoverridesdeleted', 'lesson'),
|
||||
'error' => false);
|
||||
$purgeoverrides = true;
|
||||
}
|
||||
/// updating dates - shift may be negative too
|
||||
if ($data->timeshift) {
|
||||
@@ -954,12 +958,18 @@ function lesson_reset_userdata($data) {
|
||||
WHERE lessonid IN (SELECT id FROM {lesson} WHERE course = ?)
|
||||
AND deadline <> 0", array($data->timeshift, $data->courseid));
|
||||
|
||||
$purgeoverrides = true;
|
||||
|
||||
// Any changes to the list of dates that needs to be rolled should be same during course restore and course reset.
|
||||
// See MDL-9367.
|
||||
shift_course_mod_dates('lesson', array('available', 'deadline'), $data->timeshift, $data->courseid);
|
||||
$status[] = array('component'=>$componentstr, 'item'=>get_string('datechanged'), 'error'=>false);
|
||||
}
|
||||
|
||||
if ($purgeoverrides) {
|
||||
cache::make('mod_lesson', 'overrides')->purge();
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
@@ -1497,7 +1507,7 @@ function lesson_get_coursemodule_info($coursemodule) {
|
||||
global $DB;
|
||||
|
||||
$dbparams = ['id' => $coursemodule->instance];
|
||||
$fields = 'id, name, intro, introformat, completionendreached, completiontimespent';
|
||||
$fields = 'id, name, intro, introformat, completionendreached, completiontimespent, available, deadline';
|
||||
if (!$lesson = $DB->get_record('lesson', $dbparams, $fields)) {
|
||||
return false;
|
||||
}
|
||||
@@ -1516,9 +1526,73 @@ function lesson_get_coursemodule_info($coursemodule) {
|
||||
$result->customdata['customcompletionrules']['completiontimespent'] = $lesson->completiontimespent;
|
||||
}
|
||||
|
||||
// Populate some other values that can be used in calendar or on dashboard.
|
||||
if ($lesson->available) {
|
||||
$result->customdata['available'] = $lesson->available;
|
||||
}
|
||||
if ($lesson->deadline) {
|
||||
$result->customdata['deadline'] = $lesson->deadline;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets dynamic information about a course module
|
||||
*
|
||||
* This function is called from cm_info when displaying the module
|
||||
*
|
||||
* @param cm_info $cm
|
||||
*/
|
||||
function mod_lesson_cm_info_dynamic(cm_info $cm) {
|
||||
global $USER;
|
||||
|
||||
$cache = cache::make('mod_lesson', 'overrides');
|
||||
$override = $cache->get("{$cm->instance}_u_{$USER->id}");
|
||||
|
||||
if (!$override) {
|
||||
$override = (object) [
|
||||
'available' => null,
|
||||
'deadline' => null,
|
||||
];
|
||||
}
|
||||
|
||||
// No need to look for group overrides if there are user overrides for both available and deadline.
|
||||
if (is_null($override->available) || is_null($override->deadline)) {
|
||||
$availables = [];
|
||||
$deadlines = [];
|
||||
$groupings = groups_get_user_groups($cm->course, $USER->id);
|
||||
foreach ($groupings[0] as $groupid) {
|
||||
$groupoverride = $cache->get("{$cm->instance}_g_{$groupid}");
|
||||
if (isset($groupoverride->available)) {
|
||||
$availables[] = $groupoverride->available;
|
||||
}
|
||||
if (isset($groupoverride->deadline)) {
|
||||
$deadlines[] = $groupoverride->deadline;
|
||||
}
|
||||
}
|
||||
// If there is a user override for a setting, ignore the group override.
|
||||
if (is_null($override->available) && count($availables)) {
|
||||
$override->available = min($availables);
|
||||
}
|
||||
if (is_null($override->deadline) && count($deadlines)) {
|
||||
if (in_array(0, $deadlines)) {
|
||||
$override->deadline = 0;
|
||||
} else {
|
||||
$override->deadline = max($deadlines);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Populate some other values that can be used in calendar or on dashboard.
|
||||
if (!is_null($override->available)) {
|
||||
$cm->override_customdata('available', $override->available);
|
||||
}
|
||||
if (!is_null($override->deadline)) {
|
||||
$cm->override_customdata('deadline', $override->deadline);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback which returns human-readable strings describing the active completion custom rules for the module instance.
|
||||
*
|
||||
|
||||
+10
-3
@@ -657,14 +657,14 @@ function lesson_process_group_deleted_in_course($courseid, $groupid = null) {
|
||||
if ($groupid) {
|
||||
$params['groupid'] = $groupid;
|
||||
// We just update the group that was deleted.
|
||||
$sql = "SELECT o.id, o.lessonid
|
||||
$sql = "SELECT o.id, o.lessonid, o.groupid
|
||||
FROM {lesson_overrides} o
|
||||
JOIN {lesson} lesson ON lesson.id = o.lessonid
|
||||
WHERE lesson.course = :courseid
|
||||
AND o.groupid = :groupid";
|
||||
} else {
|
||||
// No groupid, we update all orphaned group overrides for all lessons in course.
|
||||
$sql = "SELECT o.id, o.lessonid
|
||||
$sql = "SELECT o.id, o.lessonid, o.groupid
|
||||
FROM {lesson_overrides} o
|
||||
JOIN {lesson} lesson ON lesson.id = o.lessonid
|
||||
LEFT JOIN {groups} grp ON grp.id = o.groupid
|
||||
@@ -672,11 +672,15 @@ function lesson_process_group_deleted_in_course($courseid, $groupid = null) {
|
||||
AND o.groupid IS NOT NULL
|
||||
AND grp.id IS NULL";
|
||||
}
|
||||
$records = $DB->get_records_sql_menu($sql, $params);
|
||||
$records = $DB->get_records_sql($sql, $params);
|
||||
if (!$records) {
|
||||
return; // Nothing to do.
|
||||
}
|
||||
$DB->delete_records_list('lesson_overrides', 'id', array_keys($records));
|
||||
$cache = cache::make('mod_lesson', 'overrides');
|
||||
foreach ($records as $record) {
|
||||
$cache->delete("{$record->lessonid}_g_{$record->groupid}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1740,8 +1744,10 @@ class lesson extends lesson_base {
|
||||
'instance' => $this->properties->id);
|
||||
if (isset($override->userid)) {
|
||||
$conds['userid'] = $override->userid;
|
||||
$cachekey = "{$cm->instance}_u_{$override->userid}";
|
||||
} else {
|
||||
$conds['groupid'] = $override->groupid;
|
||||
$cachekey = "{$cm->instance}_g_{$override->groupid}";
|
||||
}
|
||||
$events = $DB->get_records('event', $conds);
|
||||
foreach ($events as $event) {
|
||||
@@ -1750,6 +1756,7 @@ class lesson extends lesson_base {
|
||||
}
|
||||
|
||||
$DB->delete_records('lesson_overrides', array('id' => $overrideid));
|
||||
cache::make('mod_lesson', 'overrides')->delete($cachekey);
|
||||
|
||||
// Set the common parameters for one of the events we will be triggering.
|
||||
$params = array(
|
||||
|
||||
@@ -175,6 +175,8 @@ if ($mform->is_cancelled()) {
|
||||
if (!empty($override->id)) {
|
||||
$fromform->id = $override->id;
|
||||
$DB->update_record('lesson_overrides', $fromform);
|
||||
$cachekey = $groupmode ? "{$fromform->lessonid}_g_{$fromform->groupid}" : "{$fromform->lessonid}_u_{$fromform->userid}";
|
||||
cache::make('mod_lesson', 'overrides')->delete($cachekey);
|
||||
|
||||
// Determine which override updated event to fire.
|
||||
$params['objectid'] = $override->id;
|
||||
@@ -191,6 +193,8 @@ if ($mform->is_cancelled()) {
|
||||
} else {
|
||||
unset($fromform->id);
|
||||
$fromform->id = $DB->insert_record('lesson_overrides', $fromform);
|
||||
$cachekey = $groupmode ? "{$fromform->lessonid}_g_{$fromform->groupid}" : "{$fromform->lessonid}_u_{$fromform->userid}";
|
||||
cache::make('mod_lesson', 'overrides')->delete($cachekey);
|
||||
|
||||
// Determine which override created event to fire.
|
||||
$params['objectid'] = $fromform->id;
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Contains unit tests for mod_lesson\dates.
|
||||
*
|
||||
* @package mod_lesson
|
||||
* @category test
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mod_lesson;
|
||||
|
||||
use advanced_testcase;
|
||||
use cm_info;
|
||||
use core\activity_dates;
|
||||
|
||||
/**
|
||||
* Class for unit testing mod_lesson\dates.
|
||||
*
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class dates_test extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Data provider for get_dates_for_module().
|
||||
* @return array[]
|
||||
*/
|
||||
public function get_dates_for_module_provider(): array {
|
||||
$now = time();
|
||||
$before = $now - DAYSECS;
|
||||
$earlier = $before - DAYSECS;
|
||||
$after = $now + DAYSECS;
|
||||
$later = $after + DAYSECS;
|
||||
|
||||
return [
|
||||
'without any dates' => [
|
||||
null, null, null, null, null, null, []
|
||||
],
|
||||
'only with opening time' => [
|
||||
$after, null, null, null, null, null, [
|
||||
['label' => get_string('activitydate:opens', 'course'), 'timestamp' => $after],
|
||||
]
|
||||
],
|
||||
'only with closing time' => [
|
||||
null, $after, null, null, null, null, [
|
||||
['label' => get_string('activitydate:closes', 'course'), 'timestamp' => $after],
|
||||
]
|
||||
],
|
||||
'with both times' => [
|
||||
$after, $later, null, null, null, null, [
|
||||
['label' => get_string('activitydate:opens', 'course'), 'timestamp' => $after],
|
||||
['label' => get_string('activitydate:closes', 'course'), 'timestamp' => $later],
|
||||
]
|
||||
],
|
||||
'between the dates' => [
|
||||
$before, $after, null, null, null, null, [
|
||||
['label' => get_string('activitydate:opened', 'course'), 'timestamp' => $before],
|
||||
['label' => get_string('activitydate:closes', 'course'), 'timestamp' => $after],
|
||||
]
|
||||
],
|
||||
'dates are past' => [
|
||||
$earlier, $before, null, null, null, null, [
|
||||
['label' => get_string('activitydate:opened', 'course'), 'timestamp' => $earlier],
|
||||
['label' => get_string('activitydate:closed', 'course'), 'timestamp' => $before],
|
||||
]
|
||||
],
|
||||
'with user override' => [
|
||||
$before, $after, $earlier, $later, null, null, [
|
||||
['label' => get_string('activitydate:opened', 'course'), 'timestamp' => $earlier],
|
||||
['label' => get_string('activitydate:closes', 'course'), 'timestamp' => $later],
|
||||
]
|
||||
],
|
||||
'with group override' => [
|
||||
$before, $after, null, null, $earlier, $later, [
|
||||
['label' => get_string('activitydate:opened', 'course'), 'timestamp' => $earlier],
|
||||
['label' => get_string('activitydate:closes', 'course'), 'timestamp' => $later],
|
||||
]
|
||||
],
|
||||
'with both user and group overrides' => [
|
||||
$before, $after, $earlier, $later, $earlier - DAYSECS, $later + DAYSECS, [
|
||||
['label' => get_string('activitydate:opened', 'course'), 'timestamp' => $earlier],
|
||||
['label' => get_string('activitydate:closes', 'course'), 'timestamp' => $later],
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for get_dates_for_module().
|
||||
*
|
||||
* @dataProvider get_dates_for_module_provider
|
||||
* @param int|null $available The 'available from' value of the lesson.
|
||||
* @param int|null $deadline The lesson's deadline.
|
||||
* @param int|null $useravailable The user override for opening the lesson.
|
||||
* @param int|null $userdeadline The user override for deadline of the lesson.
|
||||
* @param int|null $groupavailable The group override for opening the lesson.
|
||||
* @param int|null $groupuserdeadline The group override for deadline of the lesson.
|
||||
* @param array $expected The expected value of calling get_dates_for_module()
|
||||
*/
|
||||
public function test_get_dates_for_module(?int $available, ?int $deadline,
|
||||
?int $useravailable, ?int $userdeadline,
|
||||
?int $groupavailable, ?int $groupuserdeadline,
|
||||
array $expected) {
|
||||
$this->resetAfterTest();
|
||||
$generator = $this->getDataGenerator();
|
||||
/** @var \mod_lesson_generator $lessongenerator */
|
||||
$lessongenerator = $generator->get_plugin_generator('mod_lesson');
|
||||
|
||||
$course = $generator->create_course();
|
||||
$user = $generator->create_user();
|
||||
$generator->enrol_user($user->id, $course->id);
|
||||
|
||||
$data = ['course' => $course->id];
|
||||
if ($available) {
|
||||
$data['available'] = $available;
|
||||
}
|
||||
if ($deadline) {
|
||||
$data['deadline'] = $deadline;
|
||||
}
|
||||
$this->setAdminUser();
|
||||
$lesson = $lessongenerator->create_instance($data);
|
||||
|
||||
if ($useravailable || $userdeadline || $groupavailable || $groupuserdeadline) {
|
||||
$generator->enrol_user($user->id, $course->id);
|
||||
$group = $generator->create_group(['courseid' => $course->id]);
|
||||
$generator->create_group_member(['groupid' => $group->id, 'userid' => $user->id]);
|
||||
|
||||
if ($useravailable || $userdeadline) {
|
||||
$lessongenerator->create_override([
|
||||
'lessonid' => $lesson->id,
|
||||
'userid' => $user->id,
|
||||
'available' => $useravailable,
|
||||
'deadline' => $userdeadline,
|
||||
]);
|
||||
}
|
||||
|
||||
if ($groupavailable || $groupuserdeadline) {
|
||||
$lessongenerator->create_override([
|
||||
'lessonid' => $lesson->id,
|
||||
'groupid' => $group->id,
|
||||
'available' => $groupavailable,
|
||||
'deadline' => $groupuserdeadline,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->setUser($user);
|
||||
|
||||
$cm = get_coursemodule_from_instance('lesson', $lesson->id);
|
||||
// Make sure we're using a cm_info object.
|
||||
$cm = cm_info::create($cm);
|
||||
|
||||
$dates = activity_dates::get_dates_for_module($cm, (int) $user->id);
|
||||
|
||||
$this->assertEquals($expected, $dates);
|
||||
}
|
||||
}
|
||||
@@ -426,4 +426,27 @@ class mod_lesson_generator extends testing_module_generator {
|
||||
$page = lesson_page::create((object)$record, new lesson($lesson), $context, $CFG->maxbytes);
|
||||
return $DB->get_record('lesson_pages', array('id' => $page->id), '*', MUST_EXIST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a lesson override (either user or group).
|
||||
*
|
||||
* @param array $data must specify lessonid, and one of userid or groupid.
|
||||
*/
|
||||
public function create_override(array $data): void {
|
||||
global $DB;
|
||||
|
||||
if (!isset($data['lessonid'])) {
|
||||
throw new coding_exception('Must specify lessonid when creating a lesson override.');
|
||||
}
|
||||
|
||||
if (!isset($data['userid']) && !isset($data['groupid'])) {
|
||||
throw new coding_exception('Must specify one of userid or groupid when creating a lesson override.');
|
||||
}
|
||||
|
||||
if (isset($data['userid']) && isset($data['groupid'])) {
|
||||
throw new coding_exception('Cannot specify both userid and groupid when creating a lesson override.');
|
||||
}
|
||||
|
||||
$DB->insert_record('lesson_overrides', (object) $data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2020110900; // The current module version (Date: YYYYMMDDXX)
|
||||
$plugin->version = 2021042700; // The current module version (Date: YYYYMMDDXX)
|
||||
$plugin->requires = 2020110300; // Requires this Moodle version
|
||||
$plugin->component = 'mod_lesson'; // Full name of the plugin (used for diagnostics)
|
||||
$plugin->cron = 0;
|
||||
|
||||
Vendored
+110
@@ -0,0 +1,110 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Cache data source for the quiz overrides.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mod_quiz\cache;
|
||||
|
||||
use cache_definition;
|
||||
|
||||
/**
|
||||
* Class quiz_overrides
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class overrides implements \cache_data_source {
|
||||
|
||||
/** @var overrides the singleton instance of this class. */
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* Returns an instance of the data source class that the cache can use for loading data using the other methods
|
||||
* specified by this interface.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance_for_cache(cache_definition $definition): overrides {
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new overrides();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the data for the key provided ready formatted for caching.
|
||||
*
|
||||
* @param string|int $key The key to load.
|
||||
* @return mixed What ever data should be returned, or false if it can't be loaded.
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
public function load_for_cache($key) {
|
||||
global $DB;
|
||||
|
||||
[$quizid, $ug, $ugid] = explode('_', $key);
|
||||
$quizid = (int) $quizid;
|
||||
|
||||
switch ($ug) {
|
||||
case 'u':
|
||||
$userid = (int) $ugid;
|
||||
$override = $DB->get_record(
|
||||
'quiz_overrides',
|
||||
['quiz' => $quizid, 'userid' => $userid],
|
||||
'timeopen, timeclose, timelimit, attempts, password'
|
||||
);
|
||||
break;
|
||||
case 'g':
|
||||
$groupid = (int) $ugid;
|
||||
$override = $DB->get_record(
|
||||
'quiz_overrides',
|
||||
['quiz' => $quizid, 'groupid' => $groupid],
|
||||
'timeopen, timeclose, timelimit, attempts, password'
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new \coding_exception('Invalid cache key');
|
||||
}
|
||||
|
||||
// Return null instead of false, because false will not be cached.
|
||||
return $override ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads several keys for the cache.
|
||||
*
|
||||
* @param array $keys An array of keys each of which will be string|int.
|
||||
* @return array An array of matching data items.
|
||||
*/
|
||||
public function load_many_for_cache(array $keys) {
|
||||
$results = [];
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$results[] = $this->load_for_cache($key);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Contains the class for fetching the important dates in mod_quiz for a given module instance and a user.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mod_quiz;
|
||||
|
||||
use core\activity_dates;
|
||||
|
||||
/**
|
||||
* Class for fetching the important dates in mod_quiz for a given module instance and a user.
|
||||
*
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class dates extends activity_dates {
|
||||
|
||||
/**
|
||||
* Returns a list of important dates in mod_quiz
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_dates(): array {
|
||||
$timeopen = $this->cm->customdata['timeopen'] ?? null;
|
||||
$timeclose = $this->cm->customdata['timeclose'] ?? null;
|
||||
$now = time();
|
||||
$dates = [];
|
||||
|
||||
if ($timeopen) {
|
||||
$openlabelid = $timeopen > $now ? 'activitydate:opens' : 'activitydate:opened';
|
||||
$dates[] = [
|
||||
'label' => get_string($openlabelid, 'core_course'),
|
||||
'timestamp' => (int) $timeopen,
|
||||
];
|
||||
}
|
||||
|
||||
if ($timeclose) {
|
||||
$closelabelid = $timeclose > $now ? 'activitydate:closes' : 'activitydate:closed';
|
||||
$dates[] = [
|
||||
'label' => get_string($closelabelid, 'core_course'),
|
||||
'timestamp' => (int) $timeclose,
|
||||
];
|
||||
}
|
||||
|
||||
return $dates;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Defined caches used internally by the plugin.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$definitions = [
|
||||
'overrides' => [
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'simplekeys' => true,
|
||||
'datasource' => '\mod_quiz\cache\overrides',
|
||||
],
|
||||
];
|
||||
@@ -131,6 +131,7 @@ $string['browsersecurity_help'] = 'If "Full screen pop-up with some JavaScript s
|
||||
* The quiz will only start if the student has a JavaScript-enabled web-browser
|
||||
* The quiz appears in a full screen popup window that covers all the other windows and has no navigation controls
|
||||
* Students are prevented, as far as is possible, from using facilities like copy and paste';
|
||||
$string['cachedef_overrides'] = 'User and group override information';
|
||||
$string['calculated'] = 'Calculated';
|
||||
$string['calculatedquestion'] = 'Calculated question not supported at line {$a}. The question will be ignored';
|
||||
$string['cannotcreatepath'] = 'Path cannot be created ({$a})';
|
||||
|
||||
+79
-1
@@ -234,10 +234,12 @@ function quiz_delete_override($quiz, $overrideid, $log = true) {
|
||||
// Create the search array for a group override.
|
||||
$eventsearcharray = array('modulename' => 'quiz',
|
||||
'instance' => $quiz->id, 'groupid' => (int)$override->groupid);
|
||||
$cachekey = "{$quiz->id}_g_{$override->groupid}";
|
||||
} else {
|
||||
// Create the search array for a user override.
|
||||
$eventsearcharray = array('modulename' => 'quiz',
|
||||
'instance' => $quiz->id, 'userid' => (int)$override->userid);
|
||||
$cachekey = "{$quiz->id}_u_{$override->userid}";
|
||||
}
|
||||
$events = $DB->get_records('event', $eventsearcharray);
|
||||
foreach ($events as $event) {
|
||||
@@ -246,6 +248,7 @@ function quiz_delete_override($quiz, $overrideid, $log = true) {
|
||||
}
|
||||
|
||||
$DB->delete_records('quiz_overrides', array('id' => $overrideid));
|
||||
cache::make('mod_quiz', 'overrides')->delete($cachekey);
|
||||
|
||||
if ($log) {
|
||||
// Set the common parameters for one of the events we will be triggering.
|
||||
@@ -1538,6 +1541,8 @@ function quiz_reset_userdata($data) {
|
||||
'error' => false);
|
||||
}
|
||||
|
||||
$purgeoverrides = false;
|
||||
|
||||
// Remove user overrides.
|
||||
if (!empty($data->reset_quiz_user_overrides)) {
|
||||
$DB->delete_records_select('quiz_overrides',
|
||||
@@ -1546,6 +1551,7 @@ function quiz_reset_userdata($data) {
|
||||
'component' => $componentstr,
|
||||
'item' => get_string('useroverridesdeleted', 'quiz'),
|
||||
'error' => false);
|
||||
$purgeoverrides = true;
|
||||
}
|
||||
// Remove group overrides.
|
||||
if (!empty($data->reset_quiz_group_overrides)) {
|
||||
@@ -1555,6 +1561,7 @@ function quiz_reset_userdata($data) {
|
||||
'component' => $componentstr,
|
||||
'item' => get_string('groupoverridesdeleted', 'quiz'),
|
||||
'error' => false);
|
||||
$purgeoverrides = true;
|
||||
}
|
||||
|
||||
// Updating dates - shift may be negative too.
|
||||
@@ -1568,6 +1575,8 @@ function quiz_reset_userdata($data) {
|
||||
WHERE quiz IN (SELECT id FROM {quiz} WHERE course = ?)
|
||||
AND timeclose <> 0", array($data->timeshift, $data->courseid));
|
||||
|
||||
$purgeoverrides = true;
|
||||
|
||||
// Any changes to the list of dates that needs to be rolled should be same during course restore and course reset.
|
||||
// See MDL-9367.
|
||||
shift_course_mod_dates('quiz', array('timeopen', 'timeclose'),
|
||||
@@ -1579,6 +1588,10 @@ function quiz_reset_userdata($data) {
|
||||
'error' => false);
|
||||
}
|
||||
|
||||
if ($purgeoverrides) {
|
||||
cache::make('mod_quiz', 'overrides')->purge();
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
@@ -2079,7 +2092,8 @@ function quiz_get_coursemodule_info($coursemodule) {
|
||||
global $DB;
|
||||
|
||||
$dbparams = ['id' => $coursemodule->instance];
|
||||
$fields = 'id, name, intro, introformat, completionattemptsexhausted, completionpass, completionminattempts';
|
||||
$fields = 'id, name, intro, introformat, completionattemptsexhausted, completionpass, completionminattempts,
|
||||
timeopen, timeclose';
|
||||
if (!$quiz = $DB->get_record('quiz', $dbparams, $fields)) {
|
||||
return false;
|
||||
}
|
||||
@@ -2106,9 +2120,73 @@ function quiz_get_coursemodule_info($coursemodule) {
|
||||
$result->customdata['customcompletionrules']['completionminattempts'] = $quiz->completionminattempts;
|
||||
}
|
||||
|
||||
// Populate some other values that can be used in calendar or on dashboard.
|
||||
if ($quiz->timeopen) {
|
||||
$result->customdata['timeopen'] = $quiz->timeopen;
|
||||
}
|
||||
if ($quiz->timeclose) {
|
||||
$result->customdata['timeclose'] = $quiz->timeclose;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets dynamic information about a course module
|
||||
*
|
||||
* This function is called from cm_info when displaying the module
|
||||
*
|
||||
* @param cm_info $cm
|
||||
*/
|
||||
function mod_quiz_cm_info_dynamic(cm_info $cm) {
|
||||
global $USER;
|
||||
|
||||
$cache = cache::make('mod_quiz', 'overrides');
|
||||
$override = $cache->get("{$cm->instance}_u_{$USER->id}");
|
||||
|
||||
if (!$override) {
|
||||
$override = (object) [
|
||||
'timeopen' => null,
|
||||
'timeclose' => null,
|
||||
];
|
||||
}
|
||||
|
||||
// No need to look for group overrides if there are user overrides for both timeopen and timeclose.
|
||||
if (is_null($override->timeopen) || is_null($override->timeclose)) {
|
||||
$opens = [];
|
||||
$closes = [];
|
||||
$groupings = groups_get_user_groups($cm->course, $USER->id);
|
||||
foreach ($groupings[0] as $groupid) {
|
||||
$groupoverride = $cache->get("{$cm->instance}_g_{$groupid}");
|
||||
if (isset($groupoverride->timeopen)) {
|
||||
$opens[] = $groupoverride->timeopen;
|
||||
}
|
||||
if (isset($groupoverride->timeclose)) {
|
||||
$closes[] = $groupoverride->timeclose;
|
||||
}
|
||||
}
|
||||
// If there is a user override for a setting, ignore the group override.
|
||||
if (is_null($override->timeopen) && count($opens)) {
|
||||
$override->timeopen = min($opens);
|
||||
}
|
||||
if (is_null($override->timeclose) && count($closes)) {
|
||||
if (in_array(0, $closes)) {
|
||||
$override->timeclose = 0;
|
||||
} else {
|
||||
$override->timeclose = max($closes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Populate some other values that can be used in calendar or on dashboard.
|
||||
if (!is_null($override->timeopen)) {
|
||||
$cm->override_customdata('timeopen', $override->timeopen);
|
||||
}
|
||||
if (!is_null($override->timeclose)) {
|
||||
$cm->override_customdata('timeclose', $override->timeclose);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback which returns human-readable strings describing the active completion custom rules for the module instance.
|
||||
*
|
||||
|
||||
@@ -1923,7 +1923,7 @@ function quiz_process_group_deleted_in_course($courseid) {
|
||||
|
||||
// It would be nice if we got the groupid that was deleted.
|
||||
// Instead, we just update all quizzes with orphaned group overrides.
|
||||
$sql = "SELECT o.id, o.quiz
|
||||
$sql = "SELECT o.id, o.quiz, o.groupid
|
||||
FROM {quiz_overrides} o
|
||||
JOIN {quiz} quiz ON quiz.id = o.quiz
|
||||
LEFT JOIN {groups} grp ON grp.id = o.groupid
|
||||
@@ -1931,12 +1931,16 @@ function quiz_process_group_deleted_in_course($courseid) {
|
||||
AND o.groupid IS NOT NULL
|
||||
AND grp.id IS NULL";
|
||||
$params = array('courseid' => $courseid);
|
||||
$records = $DB->get_records_sql_menu($sql, $params);
|
||||
$records = $DB->get_records_sql($sql, $params);
|
||||
if (!$records) {
|
||||
return; // Nothing to do.
|
||||
}
|
||||
$DB->delete_records_list('quiz_overrides', 'id', array_keys($records));
|
||||
quiz_update_open_attempts(array('quizid' => array_unique(array_values($records))));
|
||||
$cache = cache::make('mod_quiz', 'overrides');
|
||||
foreach ($records as $record) {
|
||||
$cache->delete("{$record->quiz}_g_{$record->groupid}");
|
||||
}
|
||||
quiz_update_open_attempts(['quizid' => array_unique(array_column($records, 'quiz'))]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -176,6 +176,8 @@ if ($mform->is_cancelled()) {
|
||||
if (!empty($override->id)) {
|
||||
$fromform->id = $override->id;
|
||||
$DB->update_record('quiz_overrides', $fromform);
|
||||
$cachekey = $groupmode ? "{$fromform->quiz}_g_{$fromform->groupid}" : "{$fromform->quiz}_u_{$fromform->userid}";
|
||||
cache::make('mod_quiz', 'overrides')->delete($cachekey);
|
||||
|
||||
// Determine which override updated event to fire.
|
||||
$params['objectid'] = $override->id;
|
||||
@@ -192,6 +194,8 @@ if ($mform->is_cancelled()) {
|
||||
} else {
|
||||
unset($fromform->id);
|
||||
$fromform->id = $DB->insert_record('quiz_overrides', $fromform);
|
||||
$cachekey = $groupmode ? "{$fromform->quiz}_g_{$fromform->groupid}" : "{$fromform->quiz}_u_{$fromform->userid}";
|
||||
cache::make('mod_quiz', 'overrides')->delete($cachekey);
|
||||
|
||||
// Determine which override created event to fire.
|
||||
$params['objectid'] = $fromform->id;
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Contains unit tests for mod_quiz\dates.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @category test
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mod_quiz;
|
||||
|
||||
use advanced_testcase;
|
||||
use cm_info;
|
||||
use core\activity_dates;
|
||||
|
||||
/**
|
||||
* Class for unit testing mod_quiz\dates.
|
||||
*
|
||||
* @copyright 2021 Shamim Rezaie <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class dates_test extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Data provider for get_dates_for_module().
|
||||
* @return array[]
|
||||
*/
|
||||
public function get_dates_for_module_provider(): array {
|
||||
$now = time();
|
||||
$before = $now - DAYSECS;
|
||||
$earlier = $before - DAYSECS;
|
||||
$after = $now + DAYSECS;
|
||||
$later = $after + DAYSECS;
|
||||
|
||||
return [
|
||||
'without any dates' => [
|
||||
null, null, null, null, null, null, []
|
||||
],
|
||||
'only with opening time' => [
|
||||
$after, null, null, null, null, null, [
|
||||
['label' => get_string('activitydate:opens', 'course'), 'timestamp' => $after],
|
||||
]
|
||||
],
|
||||
'only with closing time' => [
|
||||
null, $after, null, null, null, null, [
|
||||
['label' => get_string('activitydate:closes', 'course'), 'timestamp' => $after],
|
||||
]
|
||||
],
|
||||
'with both times' => [
|
||||
$after, $later, null, null, null, null, [
|
||||
['label' => get_string('activitydate:opens', 'course'), 'timestamp' => $after],
|
||||
['label' => get_string('activitydate:closes', 'course'), 'timestamp' => $later],
|
||||
]
|
||||
],
|
||||
'between the dates' => [
|
||||
$before, $after, null, null, null, null, [
|
||||
['label' => get_string('activitydate:opened', 'course'), 'timestamp' => $before],
|
||||
['label' => get_string('activitydate:closes', 'course'), 'timestamp' => $after],
|
||||
]
|
||||
],
|
||||
'dates are past' => [
|
||||
$earlier, $before, null, null, null, null, [
|
||||
['label' => get_string('activitydate:opened', 'course'), 'timestamp' => $earlier],
|
||||
['label' => get_string('activitydate:closed', 'course'), 'timestamp' => $before],
|
||||
]
|
||||
],
|
||||
'with user override' => [
|
||||
$before, $after, $earlier, $later, null, null, [
|
||||
['label' => get_string('activitydate:opened', 'course'), 'timestamp' => $earlier],
|
||||
['label' => get_string('activitydate:closes', 'course'), 'timestamp' => $later],
|
||||
]
|
||||
],
|
||||
'with group override' => [
|
||||
$before, $after, null, null, $earlier, $later, [
|
||||
['label' => get_string('activitydate:opened', 'course'), 'timestamp' => $earlier],
|
||||
['label' => get_string('activitydate:closes', 'course'), 'timestamp' => $later],
|
||||
]
|
||||
],
|
||||
'with both user and group overrides' => [
|
||||
$before, $after, $earlier, $later, $earlier - DAYSECS, $later + DAYSECS, [
|
||||
['label' => get_string('activitydate:opened', 'course'), 'timestamp' => $earlier],
|
||||
['label' => get_string('activitydate:closes', 'course'), 'timestamp' => $later],
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for get_dates_for_module().
|
||||
*
|
||||
* @dataProvider get_dates_for_module_provider
|
||||
* @param int|null $timeopen Time of opening the quiz.
|
||||
* @param int|null $timeclose Time of closing the quiz.
|
||||
* @param int|null $usertimeopen The user override for opening the quiz.
|
||||
* @param int|null $usertimeclose The user override for closing the quiz.
|
||||
* @param int|null $grouptimeopen The group override for opening the quiz.
|
||||
* @param int|null $grouptimeclose The group override for closing the quiz.
|
||||
* @param array $expected The expected value of calling get_dates_for_module()
|
||||
*/
|
||||
public function test_get_dates_for_module(?int $timeopen, ?int $timeclose,
|
||||
?int $usertimeopen, ?int $usertimeclose,
|
||||
?int $grouptimeopen, ?int $grouptimeclose,
|
||||
array $expected) {
|
||||
|
||||
$this->resetAfterTest();
|
||||
$generator = $this->getDataGenerator();
|
||||
/** @var \mod_quiz_generator $quizgenerator */
|
||||
$quizgenerator = $generator->get_plugin_generator('mod_quiz');
|
||||
|
||||
$course = $generator->create_course();
|
||||
$user = $generator->create_user();
|
||||
$generator->enrol_user($user->id, $course->id);
|
||||
|
||||
$data = ['course' => $course->id];
|
||||
if ($timeopen) {
|
||||
$data['timeopen'] = $timeopen;
|
||||
}
|
||||
if ($timeclose) {
|
||||
$data['timeclose'] = $timeclose;
|
||||
}
|
||||
$quiz = $quizgenerator->create_instance($data);
|
||||
|
||||
if ($usertimeopen || $usertimeclose || $grouptimeopen || $grouptimeclose) {
|
||||
$generator->enrol_user($user->id, $course->id);
|
||||
$group = $generator->create_group(['courseid' => $course->id]);
|
||||
$generator->create_group_member(['groupid' => $group->id, 'userid' => $user->id]);
|
||||
|
||||
if ($usertimeopen || $usertimeclose) {
|
||||
$quizgenerator->create_override([
|
||||
'quiz' => $quiz->id,
|
||||
'userid' => $user->id,
|
||||
'timeopen' => $usertimeopen,
|
||||
'timeclose' => $usertimeclose,
|
||||
]);
|
||||
}
|
||||
|
||||
if ($grouptimeopen || $grouptimeclose) {
|
||||
$quizgenerator->create_override([
|
||||
'quiz' => $quiz->id,
|
||||
'groupid' => $group->id,
|
||||
'timeopen' => $grouptimeopen,
|
||||
'timeclose' => $grouptimeclose,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->setUser($user);
|
||||
|
||||
$cm = get_coursemodule_from_instance('quiz', $quiz->id);
|
||||
// Make sure we're using a cm_info object.
|
||||
$cm = cm_info::create($cm);
|
||||
|
||||
$dates = activity_dates::get_dates_for_module($cm, (int) $user->id);
|
||||
|
||||
$this->assertEquals($expected, $dates);
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2020121100;
|
||||
$plugin->version = 2021042700;
|
||||
$plugin->requires = 2020110300;
|
||||
$plugin->component = 'mod_quiz';
|
||||
|
||||
Reference in New Issue
Block a user