From 09d38e69f694ae68c2066a8351a7000ec1ea0af9 Mon Sep 17 00:00:00 2001 From: Penghai Date: Mon, 29 May 2023 15:37:04 +1000 Subject: [PATCH 1/9] feat: create a new page to do LTI 1.3 migration In the settings page, there is new link which points to the LTI 1.3 migration page. --- lang/en/equella.php | 5 +++++ lti13migration.php | 27 +++++++++++++++++++++++++++ settings.php | 4 ++++ 3 files changed, 36 insertions(+) create mode 100644 lti13migration.php diff --git a/lang/en/equella.php b/lang/en/equella.php index 02dc7fa..651b37a 100644 --- a/lang/en/equella.php +++ b/lang/en/equella.php @@ -101,6 +101,11 @@ $string['config.lti.secret.help'] = 'Client secret is required if LTI is enabled.'; $string['config.lti.liscallback.title'] = 'Outcome callback URL'; +//////////////////////////////////////////////////////// +// LTI 1.3 migration +$string['config.lti13.migration.title'] = 'LTI 1.3 Migration'; +$string['config.lti13.migration.description'] = 'Migrate openEquella resources to use LTI 1.3'; + //////////////////////////////////////////////////////// // CONFIGURATION: Shared Secrets diff --git a/lti13migration.php b/lti13migration.php new file mode 100644 index 0000000..a67a9e3 --- /dev/null +++ b/lti13migration.php @@ -0,0 +1,27 @@ +. + echo " +

Update oEQ resource links to use LTI 1.3

+
+
+ Click here to download the CSV of courses that will be affected by the migration. +
+
+ Click here to start the migration. +
+
+"; + diff --git a/settings.php b/settings.php index 0069293..c1ff126 100644 --- a/settings.php +++ b/settings.php @@ -105,4 +105,8 @@ function ecs($configoption, $params = null) { $intercepttype = new admin_setting_configselect('equella_intercept_files', get_string('interceptfiles', 'equella'), get_string('interceptfilesintro', 'equella'), 0, $choices); $settings->add($intercepttype); + + $settings->add(new admin_setting_heading('equella_lti_migration', ecs('lti13.migration.title'), '')); + $lti13MigrationUrl = new moodle_url('/mod/equella/lti13migration.php'); + $settings->add(new admin_setting_openlink('lti13migration', ecs('lti13.migration.title'), ecs('lti13.migration.description'), $lti13MigrationUrl->out())); } From a93452a1cb46d1f01473057334c970ee3c05537a Mon Sep 17 00:00:00 2001 From: Penghai Date: Tue, 30 May 2023 11:41:24 +1000 Subject: [PATCH 2/9] refactor: update the page structure to use form --- lti13migration.php | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/lti13migration.php b/lti13migration.php index a67a9e3..7e3cf7f 100644 --- a/lti13migration.php +++ b/lti13migration.php @@ -13,15 +13,28 @@ // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . + echo " -

Update oEQ resource links to use LTI 1.3

-
-
- Click here to download the CSV of courses that will be affected by the migration. -
-
- Click here to start the migration. -
-
+ +

Update oEQ resource links to use LTI 1.3

+ +

+ Before starting the migration, you can download a CSV which will list all courses which contain items that will be affected by the migration. + This CSV can be used for review, and no system modifications will occur. +

+
+ + +
+ +

+ This migration will enable existing openEQUELLA links to be opened using LTI 1.3 technology. openEQUELLA items shown in courses may display slightly differently + (e.g. use different thumbnails) after the migration. Please ensure that you have backed up your Moodle data before proceeding. +

+
+ + +
+ "; From b56f7080d04e2805cd708fe9b33021fdb80153d1 Mon Sep 17 00:00:00 2001 From: Penghai Date: Tue, 30 May 2023 11:41:39 +1000 Subject: [PATCH 3/9] chore: update comment and wording for the migration page --- lang/en/equella.php | 2 +- settings.php | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lang/en/equella.php b/lang/en/equella.php index 651b37a..385e7c8 100644 --- a/lang/en/equella.php +++ b/lang/en/equella.php @@ -104,7 +104,7 @@ //////////////////////////////////////////////////////// // LTI 1.3 migration $string['config.lti13.migration.title'] = 'LTI 1.3 Migration'; -$string['config.lti13.migration.description'] = 'Migrate openEquella resources to use LTI 1.3'; +$string['config.lti13.migration.description'] = 'Migrate openEQUELLA resources to use LTI 1.3'; //////////////////////////////////////////////////////// // CONFIGURATION: Shared Secrets diff --git a/settings.php b/settings.php index c1ff126..7ce9c88 100644 --- a/settings.php +++ b/settings.php @@ -106,6 +106,10 @@ function ecs($configoption, $params = null) { $settings->add($intercepttype); + // /////////////////////////////////////////////////////////////////////////////// + // + // LTI 1.3 migration + // $settings->add(new admin_setting_heading('equella_lti_migration', ecs('lti13.migration.title'), '')); $lti13MigrationUrl = new moodle_url('/mod/equella/lti13migration.php'); $settings->add(new admin_setting_openlink('lti13migration', ecs('lti13.migration.title'), ecs('lti13.migration.description'), $lti13MigrationUrl->out())); From cd90cd1292eabbf21712a65666a6a0c5c032de52 Mon Sep 17 00:00:00 2001 From: Penghai Date: Tue, 30 May 2023 12:30:07 +1000 Subject: [PATCH 4/9] refactor: use PHP to output the dynamic page content only --- lti13migration.php | 85 +++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/lti13migration.php b/lti13migration.php index 7e3cf7f..79e66d7 100644 --- a/lti13migration.php +++ b/lti13migration.php @@ -1,40 +1,49 @@ + + + + +LTI 1.3 Migration + + + . - - echo " - -

Update oEQ resource links to use LTI 1.3

- -

- Before starting the migration, you can download a CSV which will list all courses which contain items that will be affected by the migration. - This CSV can be used for review, and no system modifications will occur. -

-
- - -
- -

- This migration will enable existing openEQUELLA links to be opened using LTI 1.3 technology. openEQUELLA items shown in courses may display slightly differently - (e.g. use different thumbnails) after the migration. Please ensure that you have backed up your Moodle data before proceeding. -

-
- - -
- -"; +echo " +

Update oEQ resource links to use LTI 1.3

+ +

+ Before starting the migration, you can download a CSV which will list all courses which contain items that will be + affected by the migration. This CSV can be used for review, and no system modifications will occur. +

+
+ + +
+ +

+ This migration will enable existing openEQUELLA links to be opened using LTI 1.3 technology. openEQUELLA items shown + in courses may display slightly differently (e.g. use different thumbnails) after the migration. Please ensure that + you have backed up your Moodle data before proceeding. +

+
+ + +
" +?> + + From e69df06e86e1ba4c2db95d8e9af6ebcbffacf603 Mon Sep 17 00:00:00 2001 From: Penghai Date: Wed, 31 May 2023 10:55:38 +1000 Subject: [PATCH 5/9] refactor: create a new directory for LTI 1.3 migration --- lti13migration.php => lti13migration/main.php | 3 +-- settings.php | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) rename lti13migration.php => lti13migration/main.php (95%) diff --git a/lti13migration.php b/lti13migration/main.php similarity index 95% rename from lti13migration.php rename to lti13migration/main.php index 79e66d7..19eceb3 100644 --- a/lti13migration.php +++ b/lti13migration/main.php @@ -29,8 +29,7 @@ Before starting the migration, you can download a CSV which will list all courses which contain items that will be affected by the migration. This CSV can be used for review, and no system modifications will occur.

-
- +
diff --git a/settings.php b/settings.php index 7ce9c88..946fe66 100644 --- a/settings.php +++ b/settings.php @@ -111,6 +111,6 @@ function ecs($configoption, $params = null) { // LTI 1.3 migration // $settings->add(new admin_setting_heading('equella_lti_migration', ecs('lti13.migration.title'), '')); - $lti13MigrationUrl = new moodle_url('/mod/equella/lti13migration.php'); + $lti13MigrationUrl = new moodle_url('/mod/equella/lti13migration/main.php'); $settings->add(new admin_setting_openlink('lti13migration', ecs('lti13.migration.title'), ecs('lti13.migration.description'), $lti13MigrationUrl->out())); } From 65dfad5cb113a810447d674da83ba9b8e019aec9 Mon Sep 17 00:00:00 2001 From: Penghai Date: Wed, 31 May 2023 10:56:24 +1000 Subject: [PATCH 6/9] feat: add the support for downloading a CSV of courses affected by the migration --- lti13migration/downloadcsv.php | 61 ++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 lti13migration/downloadcsv.php diff --git a/lti13migration/downloadcsv.php b/lti13migration/downloadcsv.php new file mode 100644 index 0000000..184051b --- /dev/null +++ b/lti13migration/downloadcsv.php @@ -0,0 +1,61 @@ +. + +require_once('../../../config.php'); +require_once($CFG->dirroot . '/mod/lti/classes/external.php'); +require_once($CFG->dirroot . '/lib/datalib.php'); +require_once($CFG->dirroot . '/mod/equella/lib.php'); + +global $DB; + +header('Content-Type: text/csv; charset=utf-8'); +header('Content-Disposition: attachment; filename=courses.csv'); + +// Cannot use `$DB0->get_field` due to issue . +$oeqMoodleModuleID = $DB->get_field_sql("SELECT id FROM {modules} WHERE name = 'equella'"); + +// Moodle does not provide a function that can return a list of courses that contain certain specified modules. +// One would have to get a full list of courses and then filter the list by checking whether each course has +// the specified modules. +// So using the below SQL should be better. +$courseSql = "SELECT c.id, c.fullname FROM {course} c, {course_modules} cm" . + " WHERE cm.module = " . $oeqMoodleModuleID . + " AND c.id = cm.course" . + " GROUP BY c.id, c.fullname HAVING count(*) > 0"; +$courses = $DB->get_records_sql($courseSql); + +$csvHeaders = array('Course ID', 'Course name', 'Course URL'); +$csvContent = array_map(function ($course) { + $courseId = $course->id; + $courseUrl = new moodle_url('/course/view.php', array('id' => $courseId)); + return array($courseId, $course->fullname, $courseUrl->out(false)); +}, $courses); + +ob_start(); + +$outputBuffer = fopen('php://output', 'w'); +fputcsv($outputBuffer, $csvHeaders); + +foreach ($csvContent as $i => $row) { + fputcsv($outputBuffer, $row); + if ($i % 100 == 0) { + ob_flush(); + flush(); + } +} + +fclose($outputBuffer); +exit; From 9796e67ffdd91654e5e5b0ec3e41ccc9ac76922f Mon Sep 17 00:00:00 2001 From: Penghai Date: Tue, 6 Jun 2023 14:09:42 +1000 Subject: [PATCH 7/9] feat: display a drop-down to let users select which LTI platform to be used in the migration --- lti13migration/main.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lti13migration/main.php b/lti13migration/main.php index 19eceb3..5b29a16 100644 --- a/lti13migration/main.php +++ b/lti13migration/main.php @@ -22,6 +22,12 @@ get_fieldset_sql("SELECT name FROM {lti_types} WHERE ltiversion='1.3.0'"); +$options = implode(array_map(function ($name) { return ""; }, $ltiToolNames)); + echo "

Update oEQ resource links to use LTI 1.3

@@ -38,8 +44,10 @@ in courses may display slightly differently (e.g. use different thumbnails) after the migration. Please ensure that you have backed up your Moodle data before proceeding.

-
- + + + +
" ?> From 45b046aea25e358e1600dbe74478eb65c2269c29 Mon Sep 17 00:00:00 2001 From: Penghai Date: Tue, 6 Jun 2023 14:25:49 +1000 Subject: [PATCH 8/9] feat: create a new page to submit the migration task --- lti13migration/migrate.php | 47 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 lti13migration/migrate.php diff --git a/lti13migration/migrate.php b/lti13migration/migrate.php new file mode 100644 index 0000000..3801c16 --- /dev/null +++ b/lti13migration/migrate.php @@ -0,0 +1,47 @@ + out(false); +} + +$runningTasksPage = buildUrl('/admin/tool/task/runningtasks.php'); +$taskLogsPage = buildUrl('/admin/tasklogs.php'); +$purgeCachePage = buildUrl('/admin/purgecaches.php'); + +$task = new \mod_equella\task\lti13_migration_task(); +$task -> set_custom_data($_GET['ltiTypeName']); +\core\task\manager::queue_adhoc_task($task); +?> + + + +LTI 1.3 Migration + + +

Update oEQ resource links to use LTI 1.3

+ +

+ A Moodle adhoc task has been submitted to do the migration. +

+

+ However, when the task will start depends on how often the Moodle cron script is executed. +

+

+ Running tasks page, + and you can find out the task result in the Task logs page."; + ?> +

+

+ Purge caches page."; + ?> +

+ + + From 34da075c66487eea14069c13c2f8cc0bc6c65dbd Mon Sep 17 00:00:00 2001 From: Penghai Date: Tue, 6 Jun 2023 15:29:49 +1000 Subject: [PATCH 9/9] feat: create a new adhoc task to do the LTI 1.3 migration --- classes/task/lti13_migration_task.php | 132 ++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 classes/task/lti13_migration_task.php diff --git a/classes/task/lti13_migration_task.php b/classes/task/lti13_migration_task.php new file mode 100644 index 0000000..0ab7d45 --- /dev/null +++ b/classes/task/lti13_migration_task.php @@ -0,0 +1,132 @@ +. + +namespace mod_equella\task; + +use core\task\adhoc_task; +use dml_exception; +use stdClass; + +global $CFG; +require_once($CFG->dirroot . '/mod/lti/lib.php'); +require_once($CFG->dirroot . '/mod/lti/locallib.php'); + +class lti13_migration_task extends adhoc_task +{ + /** + * Create a new LTI external tool instance based on the provided course module info, details of the OEQ instance used in this + * course module and the configurations of an LTI external tool. + * + * @param $courseModule stdClass An instance of CourseModule to be updated to use LTI 1.3 as its module. + * @param $ltiToolDetails stdClass Details of the LTI external tool to be used to generate a new LTI instance. + * + * @throws dml_exception + */ + private function createLtiInstance($courseModule, $ltiToolDetails): int + { + global $DB; + + $oeqInstance = $DB->get_record('equella', array('id' => $courseModule->instance)); + $ltiConfigurations = $ltiToolDetails->configurations; + $ltiToolID = $ltiToolDetails->id; + + $lti = new stdClass(); + + $lti->typeid = $ltiToolID; + + $lti->course = $courseModule->course; + $lti->showdescriptionlaunch = $courseModule->showdescription; + $lti->coursemodule = $courseModule->id; + + $lti->icon = unserialize($oeqInstance->metadata)['thumbnail']; + $lti->intro = $oeqInstance->intro; + $lti->introformat = $oeqInstance->introformat; + $lti->name = $oeqInstance->name; + $lti->timecreated = $oeqInstance->timecreated; + $lti->timemodified = $oeqInstance->timemodified; + $lti->toolurl = $oeqInstance->url; + + $lti->instructorchoicesendname = $ltiConfigurations['sendname'] ?? 1; + $lti->instructorchoicesendmailaddr = $ltiConfigurations['sendemailaddr'] ?? 1; + $lti->instructorchoiceacceptgrades = $ltiConfigurations['acceptgrades'] == LTI_SETTING_ALWAYS ? $ltiConfigurations['acceptgrades'] : 0; + $lti->instructorchoiceallowsetting = $ltiConfigurations['ltiservice_toolsettings'] ?? null; + $lti->instructorchoiceallowroster = $ltiConfigurations['allowroster ltiservice_memberships'] ?? null; + $lti->instructorcustomparameters = $ltiConfigurations['customparameters'] ?? ""; + $lti->launchcontainer = $ltiConfigurations['launchcontainer']; + + return lti_add_instance($lti, null); // The second parameter is not used at all in this function.; + } + + /** + * Update an existing course module to use a new LTI instance. To achieve this, the value of `module` needs to be + * updated to the ID of LTI module, and the value of instance needs to be updated to the ID of a new LTI instance. + * + * @param $courseModule stdClass An instance of CourseModule to be updated to use LTI 1.3 as its module. + * @param $ltiToolDetails stdClass Details of the LTI external tool to be used to generate a new LTI instance. + * @param $ltiModuleID int ID of the LTI module. + * + * @throws dml_exception + */ + private function updateCourseModule($courseModule, $ltiToolDetails, $ltiModuleID): void + { + global $DB; + + $courseModule->module = $ltiModuleID; + $courseModule->instance = $this->createLtiInstance($courseModule, $ltiToolDetails); + $DB->update_record("course_modules", $courseModule); + } + + /** + * Return the ID of Moodle module for the provided module name. + * + * @param $moduleName string Name of a Moodle module. + * + * @throws dml_exception + */ + private function getModuleId($moduleName) { + global $DB; + return $DB->get_field_sql("SELECT id FROM {modules} WHERE name = '$moduleName'"); + } + + public function execute() + { + global $DB; + + try { + $oeqMoodleModuleID = $this->getModuleId('equella'); + $ltiModuleID = $this->getModuleId('lti'); + + $ltiTool = new stdClass(); + $ltiTypeName = $this->get_custom_data(); + $ltiTypeID = $DB->get_field_sql("SELECT id FROM {lti_types} WHERE name = '" . $ltiTypeName . "'"); + $ltiTool->id = $ltiTypeID; + $ltiTool->configurations = lti_get_type_config($ltiTypeID); + + $courseModuleList = $DB->get_records_sql("SELECT * FROM {course_modules} cm WHERE cm.module = " . $oeqMoodleModuleID); + + foreach ($courseModuleList as $cm) { + echo "Processing course ID: " . $cm->course . " openEQUELLA resource ID: " . $cm->instance . "\n"; + $this->updateCourseModule($cm, $ltiTool, $ltiModuleID); + } + + echo "LTI 1.3 migration has been successfully completed!"; + } catch (dml_exception $e) { + echo "LTI 1.3 migration failed: $e"; + } + } + + +} \ No newline at end of file