Skip to content

Commit aeaa107

Browse files
authored
Merge pull request #10 from lushonline/9-enhancement-add-functions-to-make-creationupdating-of-instances-easy
9 enhancement add functions to make creationupdating of instances easy
2 parents 6a617f7 + f402b23 commit aeaa107

16 files changed

+1265
-299
lines changed

classes/importableinstance.php

+366
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
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+
/**
18+
* Importable Instance record for mod_externalcontent
19+
*
20+
* @package mod_externalcontent
21+
* @copyright 2019-2022 LushOnline
22+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23+
*/
24+
namespace mod_externalcontent;
25+
26+
use cm_info;
27+
use context;
28+
use context_course;
29+
use context_module;
30+
use moodle_url;
31+
use stdClass;
32+
33+
/**
34+
* Importable Instance record for mod_externalcontent
35+
*
36+
* @package mod_externalcontent
37+
* @copyright 2019-2022 LushOnline
38+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39+
*/
40+
class importableinstance extends instance {
41+
42+
/**
43+
* update_course_completion_criteria
44+
*
45+
* @param stdClass $course
46+
* @param stdClass $cm
47+
* @return bool
48+
*/
49+
private static function update_course_completion_criteria(stdClass $course, stdClass $cm) : bool {
50+
global $CFG;
51+
52+
require_once($CFG->dirroot . '/completion/criteria/completion_criteria_activity.php');
53+
54+
$criterion = new \completion_criteria_activity();
55+
56+
$params = array('id' => $course->id, 'criteria_activity' => array($cm->id => 1));
57+
if ($criterion->fetch($params)) {
58+
return false;
59+
}
60+
61+
// Criteria for course.
62+
$criteriadata = new \stdClass();
63+
$criteriadata->id = $course->id;
64+
$criteriadata->criteria_activity = array($cm->id => 1);
65+
$criterion->update_config($criteriadata);
66+
67+
// Handle overall aggregation.
68+
$aggdata = array(
69+
'course' => $course->id,
70+
'criteriatype' => null,
71+
'method' => COMPLETION_AGGREGATION_ALL
72+
);
73+
74+
$aggregation = new \completion_aggregation($aggdata);
75+
$aggregation->save();
76+
77+
$aggdata['criteriatype'] = COMPLETION_CRITERIA_TYPE_ACTIVITY;
78+
$aggregation = new \completion_aggregation($aggdata);
79+
$aggregation->save();
80+
81+
$aggdata['criteriatype'] = COMPLETION_CRITERIA_TYPE_COURSE;
82+
$aggregation = new \completion_aggregation($aggdata);
83+
$aggregation->save();
84+
85+
$aggdata['criteriatype'] = COMPLETION_CRITERIA_TYPE_ROLE;
86+
$aggregation = new \completion_aggregation($aggdata);
87+
$aggregation->save();
88+
89+
return true;
90+
}
91+
92+
/**
93+
* set_course_thumbnail_from_url1
94+
*
95+
* @param instance $instance
96+
* @param string $url
97+
* @return bool
98+
*/
99+
private static function set_course_thumbnail(instance $instance, ?string $url = null) : bool {
100+
global $CFG;
101+
102+
if (is_null($url)) {
103+
return false;
104+
}
105+
106+
require_once($CFG->libdir . '/filelib.php');
107+
108+
// Check is valid url and filetype is allowed.
109+
$thumbnailurl = new moodle_url($url);
110+
$overviewfilesoptions = course_overviewfiles_options($instance->course->id);
111+
$filetypesutil = new \core_form\filetypes_util();
112+
$whitelist = $filetypesutil->normalize_file_types($overviewfilesoptions['accepted_types']);
113+
114+
$ext = pathinfo($thumbnailurl->get_path(), PATHINFO_EXTENSION);
115+
$filename = 'thumbnail.' . $ext;
116+
117+
// Check the extension is valid.
118+
if (!$filetypesutil->is_allowed_file_type($filename, $whitelist)) {
119+
return false;
120+
}
121+
122+
// Now see if we already have thumbnail.
123+
$coursecontextid = $instance->get_context_course()->id;
124+
125+
$fs = get_file_storage();
126+
127+
if ($thumbnailfile = $fs->get_file($coursecontextid, 'course', 'overviewfiles', 0, '/', $filename)) {
128+
// Check is the the file the same source as url.
129+
if ($thumbnailfile->get_source() == $url) {
130+
// Source is the same so do nothing.
131+
return false;
132+
}
133+
}
134+
135+
// Delete existing thumbnail files and continue with download.
136+
$fs->delete_area_files($coursecontextid, 'course', 'overviewfiles');
137+
138+
$thumbnailfilerecord = array(
139+
'contextid' => $coursecontextid,
140+
'component' => 'course',
141+
'filearea' => 'overviewfiles',
142+
'itemid' => '0',
143+
'filepath' => '/',
144+
'filename' => $filename,
145+
);
146+
147+
$urlparams = array(
148+
'calctimeout' => false,
149+
'timeout' => 5,
150+
'skipcertverify' => true,
151+
'connecttimeout' => 5,
152+
);
153+
154+
try {
155+
$thumbnailfile = $fs->create_file_from_url($thumbnailfilerecord, $url, $urlparams);
156+
// Check if Moodle recognises as a valid image file.
157+
if (!$thumbnailfile->is_valid_image()) {
158+
$fs->delete_area_files($coursecontextid, 'course', 'overviewfiles');
159+
return false;
160+
} else {
161+
return true;
162+
}
163+
} catch (\file_exception $e) {
164+
return false;
165+
}
166+
}
167+
168+
/**
169+
* Get the current course tags as array sorted alphbetically
170+
*
171+
* @param int $courseid
172+
* @return array
173+
*/
174+
private static function get_course_tags(int $courseid) : array {
175+
$tags = array();
176+
if (\core_tag_tag::is_enabled('core', 'course')) {
177+
$coursetags = \core_tag_tag::get_item_tags_array('core', 'course', $courseid,
178+
\core_tag_tag::BOTH_STANDARD_AND_NOT, 0, false);
179+
180+
foreach ($coursetags as $value) {
181+
array_push($tags, $value);
182+
}
183+
}
184+
return $tags;
185+
}
186+
187+
/**
188+
* create_from_importrecord
189+
*
190+
* @param importrecord $importrecord
191+
* @return null|instance
192+
*/
193+
private static function create_from_importrecord(importrecord $importrecord) : ?instance {
194+
global $DB, $CFG;
195+
196+
require_once($CFG->dirroot . '/course/lib.php');
197+
require_once($CFG->libdir . '/phpunit/classes/util.php');
198+
require_once($CFG->dirroot . '/mod/externalcontent/lib.php');
199+
200+
$messages = array();
201+
202+
// We need to create.
203+
$generator = \phpunit_util::get_data_generator();
204+
205+
$courseimport = clone $importrecord->get_courseimport();
206+
$moduleimport = clone $importrecord->get_moduleimport();
207+
208+
$course = create_course($courseimport);
209+
$moduleimport->course = $course->id;
210+
211+
$messages[] = 'Course: created.';
212+
$messages[] = $courseimport->visible ? 'Course Visibility Updated: visible.' : 'Course Visibility Updated: hidden.';
213+
214+
$instance = $generator->create_module('externalcontent', $moduleimport);
215+
216+
$cm = get_coursemodule_from_instance('externalcontent', $instance->id);
217+
$cm->idnumber = $course->idnumber;
218+
$DB->update_record('course_modules', $cm);
219+
$messages[] = 'Module: created.';
220+
221+
if (self::update_course_completion_criteria($course, $cm)) {
222+
$messages[] = 'Course: completion criteria created.';
223+
};
224+
225+
$newinstance = self::get_from_cmid($cm->id);
226+
// Set the tags.
227+
if (\core_tag_tag::is_enabled('core', 'course') && count($courseimport->tags) > 0) {
228+
\core_tag_tag::set_item_tags('core', 'course', $newinstance->get_course_id(),
229+
$newinstance->get_context_course(), $courseimport->tags);
230+
$messages[] = 'Course Property: tags created.';
231+
}
232+
233+
if (self::set_course_thumbnail($newinstance, $courseimport->thumbnail)) {
234+
$messages[] = 'Course: thumbnail created.';
235+
};
236+
237+
$newinstance->clear_messages();
238+
$newinstance->set_messages($messages);
239+
return $newinstance;
240+
}
241+
242+
/**
243+
* update_from_importrecord
244+
*
245+
* @param instance $instance
246+
* @param importrecord $importrecord
247+
* @return null|instance
248+
*/
249+
private static function update_from_importrecord(instance $instance, importrecord $importrecord) : ?instance {
250+
global $DB, $CFG;
251+
252+
require_once($CFG->dirroot . '/course/lib.php');
253+
require_once($CFG->libdir . '/phpunit/classes/util.php');
254+
require_once($CFG->dirroot . '/mod/externalcontent/lib.php');
255+
256+
$messages = array();
257+
258+
$courseimport = clone $importrecord->get_courseimport();
259+
260+
if ($instance->is_course_visible() != (bool)$courseimport->visible) {
261+
$messages[] = $courseimport->visible ? 'Course Visibility Updated: visible.' : 'Course Visibility Updated: hidden.';
262+
}
263+
264+
$courseupdateneeded = false;
265+
foreach ($instance->course as $k => $v) {
266+
if (isset($courseimport->$k) && !in_array($k, $courseimport->readonly)) {
267+
if ($instance->course->$k != $courseimport->$k) {
268+
$instance->course->$k = $courseimport->$k;
269+
$messages[] = 'Course Property: '.$k.' updated';
270+
$courseupdateneeded = true;
271+
};
272+
}
273+
}
274+
275+
// Update the course object.
276+
if ($courseupdateneeded) {
277+
update_course($instance->course);
278+
}
279+
280+
// Update tags.
281+
if (\core_tag_tag::is_enabled('core', 'course') && count($courseimport->tags) > 0) {
282+
$existingtags = self::get_course_tags($instance->get_course_id());
283+
284+
if (!$tagsmatch = (
285+
array_diff($existingtags, $courseimport->tags) == [] &&
286+
array_diff($courseimport->tags, $existingtags) == [])) {
287+
\core_tag_tag::set_item_tags('core', 'course',
288+
$instance->get_course_id(),
289+
$instance->get_context_course(),
290+
$courseimport->tags);
291+
$messages[] = 'Course Property: tags updated.';
292+
}
293+
}
294+
295+
$moduleimport = clone $importrecord->get_moduleimport();
296+
297+
$moduleupdateneeded = false;
298+
foreach ($instance->module as $k => $v) {
299+
if (isset($moduleimport->$k) && !in_array($k, $moduleimport->readonly)) {
300+
if ($instance->module->$k != $moduleimport->$k) {
301+
$instance->module->$k = $moduleimport->$k;
302+
$messages[] = 'Module Property: '.$k.' updated';
303+
$moduleupdateneeded = true;
304+
}
305+
}
306+
}
307+
308+
// Update the externalcontent object.
309+
if ($moduleupdateneeded) {
310+
$DB->update_record('externalcontent', $instance->module);
311+
}
312+
313+
// Update the cm.
314+
$cm = get_coursemodule_from_instance('externalcontent', $instance->module->id);
315+
316+
if ($cm->idnumber != $instance->course->idnumber) {
317+
$cm->idnumber = $instance->course->idnumber;
318+
$DB->update_record('course_modules', $cm);
319+
$messages[] = 'Module Property: idnumber updated';
320+
};
321+
322+
if (self::update_course_completion_criteria($instance->course, $cm)) {
323+
$messages[] = 'Course: completion criteria updated.';
324+
};
325+
326+
if (self::set_course_thumbnail($instance, $courseimport->thumbnail)) {
327+
$messages[] = 'Course: thumbnail updated.';
328+
};
329+
330+
$newinstance = self::get_from_cmid($cm->id);
331+
$newinstance->clear_messages();
332+
$newinstance->set_messages(count($messages) > 0 ? $messages : ['No changes']);
333+
return $newinstance;
334+
}
335+
336+
/**
337+
* Get the instance information from an import record.
338+
* This will upsert an instance.
339+
*
340+
* @param importrecord $importrecord
341+
* @return null|instance
342+
*/
343+
public static function get_from_importrecord(importrecord $importrecord): ?instance {
344+
if ($valid = $importrecord->validate()) {
345+
if ($result = self::get_from_cmidnumber($importrecord->get_courseimport()->idnumber)) {
346+
// Update.
347+
return self::update_from_importrecord($result, $importrecord);
348+
} else {
349+
// Create.
350+
return self::create_from_importrecord($importrecord);
351+
}
352+
} else {
353+
return null;
354+
}
355+
}
356+
357+
/**
358+
* set_course_thumbnail_from_url
359+
*
360+
* @param string $url
361+
* @return bool
362+
*/
363+
public function set_course_thumbnail_from_url(string $url): bool {
364+
return self::set_course_thumbnail($this, $url);
365+
}
366+
}

0 commit comments

Comments
 (0)