Skip to content

Commit e619a01

Browse files
authored
Merge pull request #131 from catalyst/issue121-401
Issue #121 Update old records from system context to cm context.
2 parents e284319 + c4971ef commit e619a01

File tree

4 files changed

+296
-1
lines changed

4 files changed

+296
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
// This file is part of Moodle - http://moodle.org/
3+
//
4+
// Moodle is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// Moodle is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16+
17+
namespace mod_cms\task;
18+
19+
use core\task\adhoc_task;
20+
21+
/**
22+
* Runs customfield context update.
23+
*
24+
* @package mod_cms
25+
* @author Tomo Tsuyuki <[email protected]>
26+
* @copyright 2023 Catalyst IT
27+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28+
*/
29+
class update_customfield_context extends adhoc_task {
30+
31+
/**
32+
* Run the task.
33+
*
34+
* @return void
35+
*/
36+
public function execute() {
37+
global $DB;
38+
39+
// Record {customfield_data}.instanceid is from {cms}.id.
40+
// Record {customfield_data}.contextid is from contextid of {course_modules}, which is linked to {cms}.
41+
42+
// Collect records which have wrong contextid in the customfield data.
43+
$sql = "SELECT mcd.id mcdid, mcd.contextid mcdcontextid, mc.id mcid
44+
FROM {customfield_data} mcd
45+
JOIN {customfield_field} mcf ON mcf.id = mcd.fieldid
46+
JOIN {customfield_category} mcc ON mcc.id = mcf.categoryid
47+
JOIN {course_modules} mcm ON mcm.instance = mcd.instanceid AND mcm.module = (
48+
SELECT id FROM {modules} WHERE name = 'cms'
49+
)
50+
JOIN {context} mc ON mc.instanceid = mcm.id AND contextlevel = " . CONTEXT_MODULE . "
51+
WHERE mcc.component = 'mod_cms' AND mcc.area = 'cmsfield' AND mcd.contextid != mc.id";
52+
$records = $DB->get_records_sql($sql);
53+
// Update records with correct contextid.
54+
foreach ($records as $record) {
55+
$DB->set_field('customfield_data', 'contextid', $record->mcid, ['id' => $record->mcdid]);
56+
}
57+
}
58+
}

classes/task/update_files_context.php

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
<?php
2+
// This file is part of Moodle - https://moodle.org/
3+
//
4+
// Moodle is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// Moodle is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
16+
17+
namespace mod_cms\task;
18+
19+
use core\task\adhoc_task;
20+
use csv_export_writer;
21+
use file_storage;
22+
use moodle_exception;
23+
24+
/**
25+
* Update the context of embedded files to match the context of the mod_cms module instance.
26+
*
27+
* @package mod_cms
28+
* @author Alexander Van der Bellen <[email protected]>
29+
* @copyright 2024 Catalyst IT Australia
30+
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
31+
*/
32+
class update_files_context extends adhoc_task {
33+
34+
/**
35+
* Factory method to create a new update_files_context task.
36+
*
37+
* @param int|null $courseid Limit the task to a specific course or all courses if null.
38+
* @param bool $dryrun Whether to run the task without making database changes.
39+
* @return update_files_context The task instance.
40+
*/
41+
public static function instance(?int $courseid = null, bool $dryrun = true): update_files_context {
42+
$task = new self();
43+
$task->set_custom_data((object) ['courseid' => $courseid, 'dryrun' => $dryrun]);
44+
return $task;
45+
}
46+
47+
/**
48+
* Run the task to delete course results for a user.
49+
*/
50+
public function execute(): void {
51+
global $CFG;
52+
$data = $this->get_custom_data();
53+
$csv = self::update_contexts($data->courseid, $data->dryrun);
54+
file_put_contents($CFG->dataroot . '/' . $csv->filename, $csv->print_csv_data(true));
55+
}
56+
57+
/**
58+
* Update mod_cms customfield_textarea embedded file contexts to match the context of the mod_cms module instance.
59+
* @param int|null $courseid Limit the task to a specific course or all courses if null.
60+
* @param bool $dryrun Whether to run the task without making database changes.
61+
* @return csv_export_writer The CSV export writer containing the results of the task.
62+
*/
63+
private static function update_contexts(?int $courseid, bool $dryrun): csv_export_writer {
64+
global $CFG, $DB;
65+
66+
require_once($CFG->libdir . '/csvlib.class.php');
67+
68+
$sql = "SELECT f.*, ctx.id AS ctxid, cms.course AS courseid
69+
FROM {files} f
70+
JOIN {customfield_data} cfd ON cfd.id = f.itemid
71+
JOIN {customfield_field} cff ON cff.id = cfd.fieldid
72+
JOIN {cms} cms ON cms.id = cfd.instanceid
73+
JOIN {cms_types} cmst ON cmst.id = cms.typeid AND cmst.datasources LIKE '%fields%'
74+
JOIN {course_modules} cm ON cm.instance = cms.id
75+
JOIN {modules} m ON m.id = cm.module AND m.name = 'cms'
76+
JOIN {context} ctx ON ctx.instanceid = cm.id AND ctx.contextlevel = :modulecontextlevel
77+
WHERE f.contextid = 1 AND f.component = 'customfield_textarea' AND f.filearea = 'value'";
78+
79+
$params = ['modulecontextlevel' => CONTEXT_MODULE];
80+
81+
if (!empty($courseid)) {
82+
$sql .= " AND cms.course = :courseid";
83+
$params['courseid'] = $courseid;
84+
}
85+
86+
$csv = new csv_export_writer();
87+
$csv->set_filename('mod_cms_update_files_context');
88+
$csv->add_data([
89+
'courseid',
90+
'newpathnamehash',
91+
'newcontextid',
92+
'id',
93+
'contenthash',
94+
'pathnamehash',
95+
'contextid',
96+
'component',
97+
'filearea',
98+
'itemid',
99+
'filepath',
100+
'filename',
101+
'timecreated',
102+
'timemodified',
103+
]);
104+
105+
$records = $DB->get_recordset_sql($sql, $params);
106+
foreach ($records as $record) {
107+
$newcontextid = $record->ctxid;
108+
109+
$newpathnamehash = file_storage::get_pathname_hash(
110+
$newcontextid,
111+
$record->component,
112+
$record->filearea,
113+
$record->itemid,
114+
$record->filepath,
115+
$record->filename
116+
);
117+
118+
$csv->add_data([
119+
$record->courseid,
120+
$newpathnamehash,
121+
$newcontextid,
122+
$record->id,
123+
$record->contenthash,
124+
$record->pathnamehash,
125+
$record->contextid,
126+
$record->component,
127+
$record->filearea,
128+
$record->itemid,
129+
$record->filepath,
130+
$record->filename,
131+
$record->timecreated,
132+
$record->timemodified,
133+
]);
134+
135+
// Update the record with the new context id and path name hash.
136+
$record->contextid = $newcontextid;
137+
$record->pathnamehash = $newpathnamehash;
138+
139+
if (!$dryrun) {
140+
// Remove the ctxid and courseid fields.
141+
unset($record->ctxid);
142+
unset($record->courseid);
143+
144+
// Update the record in the database.
145+
try {
146+
$DB->update_record('files', $record);
147+
} catch (moodle_exception $e) {
148+
debugging('Failed to insert record into files table: ' . $e->getMessage(), DEBUG_DEVELOPER);
149+
}
150+
}
151+
}
152+
153+
$records->close();
154+
155+
return $csv;
156+
}
157+
}

db/upgrade.php

+80
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@
2323
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2424
*/
2525

26+
use core\task\manager;
2627
use mod_cms\local\lib;
2728
use mod_cms\local\model\cms_types;
2829
use mod_cms\local\model\cms;
2930
use mod_cms\local\datasource\fields;
3031
use mod_cms\local\datasource\userlist;
32+
use mod_cms\task\update_customfield_context;
3133

3234
/**
3335
* Function to upgrade mod_cms database
@@ -341,5 +343,83 @@ function xmldb_cms_upgrade($oldversion) {
341343
upgrade_mod_savepoint(true, 2023112100, 'cms');
342344
}
343345

346+
if ($oldversion < 2024090301) {
347+
// Add adhoc task to update contextid in customfield records for 'cmsfield' type.
348+
manager::queue_adhoc_task(new update_customfield_context());
349+
upgrade_mod_savepoint(true, 2024090301, 'cms');
350+
}
351+
352+
if ($oldversion < 2024090302) {
353+
$dbman = $DB->get_manager();
354+
// Update valuetrust if exists for mod_cms (both 'cmsfield' and 'cmsuserlist').
355+
if ($dbman->field_exists('customfield_data', 'valuetrust')) {
356+
$sql = "SELECT mcd.id mcdid
357+
FROM {customfield_data} mcd
358+
JOIN {customfield_field} mcf ON mcf.id = mcd.fieldid
359+
JOIN {customfield_category} mcc ON mcc.id = mcf.categoryid
360+
WHERE mcc.component = 'mod_cms' AND mcd.valuetrust = 0";
361+
$records = $DB->get_records_sql($sql);
362+
$mcdids = array_keys($records);
363+
foreach (array_chunk($mcdids, 1000) as $ids) {
364+
[$sql, $params] = $DB->get_in_or_equal($ids);
365+
$sql = 'UPDATE {customfield_data} SET valuetrust = 1 WHERE id ' . $sql;
366+
$DB->execute($sql, $params);
367+
}
368+
}
369+
upgrade_mod_savepoint(true, 2024090302, 'cms');
370+
}
371+
372+
if ($oldversion < 2024090303) {
373+
// Update contextid in customfield records for 'cmsuserlist' type.
374+
// {customfield_data}.instanceid" is from one of id from userlistinstanceids which is JSON encoded in {cms}.customdata.
375+
// "userlistinstanceids" is a unique id for the customfield_data.
376+
// New ID is from userlistmaxinstanceid which is JSON encoded in the {cms_types}.customdata" and add 1 to use.
377+
$sql = "SELECT mc.id, mc.customdata, mcx.id contextid
378+
FROM {cms} mc
379+
JOIN {course_modules} mcm ON mc.id = mcm.instance AND mcm.module = (
380+
SELECT id FROM {modules} WHERE name = 'cms'
381+
)
382+
JOIN {context} mcx ON mcx.instanceid = mcm.id AND mcx.contextlevel = " . CONTEXT_MODULE . "
383+
WHERE mc.customdata LIKE '%userlistinstanceids%'";
384+
$cmsrecords = $DB->get_records_sql($sql);
385+
386+
// Load userlist from customfield_data and set to array.
387+
$userlist = [];
388+
foreach ($cmsrecords as $cmsrecord) {
389+
$customdata = json_decode($cmsrecord->customdata);
390+
foreach ($customdata->userlistinstanceids as $instanceid) {
391+
$userlist[$instanceid] = [
392+
'id' => $cmsrecord->id,
393+
'contextid' => $cmsrecord->contextid,
394+
];
395+
}
396+
}
397+
398+
// Check cmsuserlist records and set correct contextid if it's different one.
399+
$sql = "SELECT mcd.id, mcd.instanceid, mcd.contextid
400+
FROM {customfield_data} mcd
401+
JOIN {customfield_field} mcf ON mcf.id = mcd.fieldid
402+
JOIN {customfield_category} mcc ON mcc.id = mcf.categoryid
403+
WHERE mcc.component = 'mod_cms' AND mcc.area = 'cmsuserlist'";
404+
$cmsuserlistdata = $DB->get_records_sql($sql);
405+
foreach ($cmsuserlistdata as $cmsuserlist) {
406+
if (!empty($userlist[$cmsuserlist->instanceid])) {
407+
if ($userlist[$cmsuserlist->instanceid]['contextid'] != $cmsuserlist->contextid) {
408+
$DB->set_field('customfield_data', 'contextid', $userlist[$cmsuserlist->instanceid]['contextid'],
409+
['id' => $cmsuserlist->id]);
410+
}
411+
}
412+
}
413+
upgrade_mod_savepoint(true, 2024090303, 'cms');
414+
}
415+
416+
if ($oldversion < 2024090305) {
417+
// Run ad hoc task for updating contextid in the files table.
418+
$task = \mod_cms\task\update_files_context::instance(null, false);
419+
manager::queue_adhoc_task($task);
420+
421+
upgrade_mod_savepoint(true, 2024090305, 'cms');
422+
}
423+
344424
return true;
345425
}

version.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
defined('MOODLE_INTERNAL') || die();
2727

28-
$plugin->version = 2024090301;
28+
$plugin->version = 2024090305;
2929
$plugin->requires = 2022112800; // Moodle 4.1 and above.
3030
$plugin->supported = [401, 401]; // Moodle 4.1.
3131
$plugin->component = 'mod_cms';

0 commit comments

Comments
 (0)