Skip to content

Commit 7867983

Browse files
authored
Merge pull request #139 from catalyst/issue121-main-ad-hoc-task
[Issue #121] Files table context ad hoc task
2 parents 60f5426 + 7a15826 commit 7867983

File tree

2 files changed

+158
-41
lines changed

2 files changed

+158
-41
lines changed

classes/task/update_files_context.php

+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
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 $DB;
65+
66+
$sql = "SELECT f.*, ctx.id AS ctxid, cms.course AS courseid
67+
FROM {files} f
68+
JOIN {customfield_data} cfd ON cfd.id = f.itemid
69+
JOIN {customfield_field} cff ON cff.id = cfd.fieldid
70+
JOIN {cms} cms ON cms.id = cfd.instanceid
71+
JOIN {cms_types} cmst ON cmst.id = cms.typeid AND cmst.datasources LIKE '%fields%'
72+
JOIN {course_modules} cm ON cm.instance = cms.id
73+
JOIN {modules} m ON m.id = cm.module AND m.name = 'cms'
74+
JOIN {context} ctx ON ctx.instanceid = cm.id AND ctx.contextlevel = :modulecontextlevel
75+
WHERE f.contextid = 1 AND f.component = 'customfield_textarea' AND f.filearea = 'value'";
76+
77+
$params = ['modulecontextlevel' => CONTEXT_MODULE];
78+
79+
if (!empty($courseid)) {
80+
$sql .= " AND cms.course = :courseid";
81+
$params['courseid'] = $courseid;
82+
}
83+
84+
$csv = new csv_export_writer();
85+
$csv->set_filename('mod_cms_update_files_context');
86+
$csv->add_data([
87+
'courseid',
88+
'newpathnamehash',
89+
'newcontextid',
90+
'id',
91+
'contenthash',
92+
'pathnamehash',
93+
'contextid',
94+
'component',
95+
'filearea',
96+
'itemid',
97+
'filepath',
98+
'filename',
99+
'timecreated',
100+
'timemodified',
101+
]);
102+
103+
$records = $DB->get_recordset_sql($sql, $params);
104+
foreach ($records as $record) {
105+
$newcontextid = $record->ctxid;
106+
107+
$newpathnamehash = file_storage::get_pathname_hash(
108+
$newcontextid,
109+
$record->component,
110+
$record->filearea,
111+
$record->itemid,
112+
$record->filepath,
113+
$record->filename
114+
);
115+
116+
$csv->add_data([
117+
$record->courseid,
118+
$newpathnamehash,
119+
$newcontextid,
120+
$record->id,
121+
$record->contenthash,
122+
$record->pathnamehash,
123+
$record->contextid,
124+
$record->component,
125+
$record->filearea,
126+
$record->itemid,
127+
$record->filepath,
128+
$record->filename,
129+
$record->timecreated,
130+
$record->timemodified,
131+
]);
132+
133+
// Update the record with the new context id and path name hash.
134+
$record->contextid = $newcontextid;
135+
$record->pathnamehash = $newpathnamehash;
136+
137+
if (!$dryrun) {
138+
// Remove the ctxid and courseid fields.
139+
unset($record->ctxid);
140+
unset($record->courseid);
141+
142+
// Update the record in the database.
143+
try {
144+
$DB->update_record('files', $record);
145+
} catch (moodle_exception $e) {
146+
debugging('Failed to insert record into files table: ' . $e->getMessage(), DEBUG_DEVELOPER);
147+
}
148+
}
149+
}
150+
151+
$records->close();
152+
153+
return $csv;
154+
}
155+
}

db/upgrade.php

+3-41
Original file line numberDiff line numberDiff line change
@@ -414,47 +414,9 @@ function xmldb_cms_upgrade($oldversion) {
414414
}
415415

416416
if ($oldversion < 2024090304) {
417-
// Update files belonging to mod_cms overview section content types to use the course module context id.
418-
// Update the pathnamehash as well, otherwise files will display as missing until the content is edited and saved.
419-
// Records are effectively copied, the old records remain in case anything relies on them and they can be used to
420-
// cross reference the new records if needed.
421-
422-
$sql = "SELECT f.*, ctx.id as ctxid
423-
FROM {files} f
424-
JOIN {customfield_data} cfd ON cfd.id = f.itemid
425-
JOIN {customfield_field} cff ON cff.id = cfd.fieldid
426-
JOIN {cms} cms ON cms.id = cfd.instanceid
427-
JOIN {cms_types} cmst ON cmst.id = cms.typeid AND cmst.datasources LIKE '%fields%'
428-
JOIN {course_modules} cm ON cm.instance = cms.id
429-
JOIN {modules} m ON m.id = cm.module AND m.name = 'cms'
430-
JOIN {context} ctx ON ctx.instanceid = cm.id AND ctx.contextlevel = :modulecontextlevel
431-
WHERE f.contextid = 1 AND f.component = 'customfield_textarea' AND f.filearea = 'value'";
432-
$params = ['modulecontextlevel' => CONTEXT_MODULE];
433-
434-
$records = $DB->get_recordset_sql($sql, $params);
435-
foreach ($records as $record) {
436-
// Update the record with the new context id and path name hash.
437-
$record->contextid = $record->ctxid;
438-
$record->pathnamehash = file_storage::get_pathname_hash(
439-
$record->contextid,
440-
$record->component,
441-
$record->filearea,
442-
$record->itemid,
443-
$record->filepath,
444-
$record->filename
445-
);
446-
447-
// Remove the record id and ctxid fields.
448-
unset($record->ctxid);
449-
unset($record->id);
450-
451-
// Create a new record.
452-
try {
453-
$DB->insert_record('files', $record);
454-
} catch (moodle_exception $e) {
455-
debugging('Failed to insert record into files table: ' . $e->getMessage(), DEBUG_DEVELOPER);
456-
}
457-
}
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);
458420

459421
upgrade_mod_savepoint(true, 2024090304, 'cms');
460422
}

0 commit comments

Comments
 (0)