Skip to content

Commit

Permalink
MDL-71410 mod_assign: Cache user and group overrides
Browse files Browse the repository at this point in the history
  • Loading branch information
rezaies committed Apr 27, 2021
1 parent 14e3759 commit 8e50d64
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 7 deletions.
110 changes: 110 additions & 0 deletions mod/assign/classes/cache/overrides.php
@@ -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 <shamim@moodle.com>
* @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 <shamim@moodle.com>
* @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;
}
}
35 changes: 35 additions & 0 deletions mod/assign/db/caches.php
@@ -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 <shamim@moodle.com>
* @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',
],
];
1 change: 1 addition & 0 deletions mod/assign/lang/en/assign.php
Expand Up @@ -112,6 +112,7 @@
$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';
Expand Down
36 changes: 30 additions & 6 deletions mod/assign/locallib.php
Expand Up @@ -874,8 +874,10 @@ public function delete_override($overrideid) {
$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) {
Expand All @@ -884,6 +886,7 @@ public function delete_override($overrideid) {
}

$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(
Expand Down Expand Up @@ -1203,6 +1206,8 @@ public function reset_userdata($data) {
}
}

$purgeoverrides = false;

// Remove user overrides.
if (!empty($data->reset_assign_user_overrides)) {
$DB->delete_records_select('assign_overrides',
Expand All @@ -1211,6 +1216,7 @@ public function reset_userdata($data) {
'component' => $componentstr,
'item' => get_string('useroverridesdeleted', 'assign'),
'error' => false);
$purgeoverrides = true;
}
// Remove group overrides.
if (!empty($data->reset_assign_group_overrides)) {
Expand All @@ -1220,6 +1226,7 @@ public function reset_userdata($data) {
'component' => $componentstr,
'item' => get_string('groupoverridesdeleted', 'assign'),
'error' => false);
$purgeoverrides = true;
}

// Updating dates - shift may be negative too.
Expand All @@ -1237,6 +1244,8 @@ public function reset_userdata($data) {
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',
Expand All @@ -1248,6 +1257,10 @@ public function reset_userdata($data) {
'error'=>false);
}

if ($purgeoverrides) {
cache::make('mod_assign', 'overrides')->purge();
}

return $status;
}

Expand Down Expand Up @@ -9790,26 +9803,30 @@ 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
WHERE assign.course = :courseid
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}");
}
}

/**
Expand All @@ -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.
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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 = [
Expand Down
4 changes: 4 additions & 0 deletions mod/assign/overrideedit.php
Expand Up @@ -187,6 +187,8 @@
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;
Expand Down Expand Up @@ -219,6 +221,8 @@
$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;
Expand Down
2 changes: 1 addition & 1 deletion mod/assign/version.php
Expand Up @@ -25,5 +25,5 @@
defined('MOODLE_INTERNAL') || die();

$plugin->component = 'mod_assign'; // Full name of the plugin (used for diagnostics).
$plugin->version = 2021052500; // The current module version (Date: YYYYMMDDXX).
$plugin->version = 2021052501; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2021052500; // Requires this Moodle version.

0 comments on commit 8e50d64

Please sign in to comment.