From be578a38ec6eb00b7d6a0b0d83901c24483ed2ee Mon Sep 17 00:00:00 2001 From: Sarathlal Sarangadharan Date: Tue, 5 Aug 2025 11:38:14 +0100 Subject: [PATCH] TD-5858: 2 new end points are created. 1. For 6 month course of a specific user 2. for history of user 3. additional columns for MyLearning page --- local/mylearningservice/db/services.php | 31 +++ local/mylearningservice/externallib.php | 257 ++++++++++++++++++ .../lang/en/local_mylearningservice.php | 2 + local/mylearningservice/version.php | 8 + 4 files changed, 298 insertions(+) create mode 100644 local/mylearningservice/db/services.php create mode 100644 local/mylearningservice/externallib.php create mode 100644 local/mylearningservice/lang/en/local_mylearningservice.php create mode 100644 local/mylearningservice/version.php diff --git a/local/mylearningservice/db/services.php b/local/mylearningservice/db/services.php new file mode 100644 index 00000000000..b4e9735b43d --- /dev/null +++ b/local/mylearningservice/db/services.php @@ -0,0 +1,31 @@ + array( + 'classname' => 'mylearningservice_external', + 'methodname' => 'get_recent_courses', + 'classpath' => 'local/mylearningservice/externallib.php', + 'description' => 'Get courses a user was enrolled in within the last 6 months', + 'type' => 'read', + 'ajax' => true, + 'capabilities' => 'moodle/course:view', + ), +); +$services = array( + 'Get Recent Courses' => array( + 'functions' => array( + + 'mylearningservice_get_recent_courses' + ), + 'restrictedusers' => 0, + 'enabled' => 1, + // This field os optional, but requried if the `restrictedusers` value is + // set, so as to allow configuration via the Web UI. + 'shortname' => 'GetRecentCourses', + + // Whether to allow file downloads. + 'downloadfiles' => 0, + + // Whether to allow file uploads. + 'uploadfiles' => 0, + ) +); \ No newline at end of file diff --git a/local/mylearningservice/externallib.php b/local/mylearningservice/externallib.php new file mode 100644 index 00000000000..04af6b8e8e4 --- /dev/null +++ b/local/mylearningservice/externallib.php @@ -0,0 +1,257 @@ +libdir/externallib.php"); + +class mylearningservice_external extends external_api { + public static function get_recent_courses_parameters() { + return new external_function_parameters( + array( + 'userid' => new external_value(PARAM_INT, 'User ID'), + // Allow omission by setting VALUE_DEFAULT and defaulting to 0 (meaning no limit) + 'months' => new external_value(PARAM_INT, 'Number of past months to include', VALUE_DEFAULT, 0), + ) + ); +} + + public static function get_recent_courses($userid, $months = 0) { + global $CFG, $USER, $DB, $OUTPUT; + + require_once($CFG->dirroot . '/course/lib.php'); + require_once($CFG->dirroot . '/user/lib.php'); + require_once($CFG->libdir . '/completionlib.php'); + + // Do basic automatic PARAM checks on incoming data, using params description + // If any problems are found then exceptions are thrown with helpful error messages + $returnusercount = true; + + $courses = enrol_get_users_courses($userid, true, '*'); + $result = array(); + + // Get user data including last access to courses. + $user = get_complete_user_data('id', $userid); + $sameuser = $USER->id == $userid; + + // Retrieve favourited courses (starred). + $favouritecourseids = array(); + if ($sameuser) { + $ufservice = \core_favourites\service_factory::get_service_for_user_context(\context_user::instance($userid)); + $favourites = $ufservice->find_favourites_by_type('core_course', 'courses'); + + if ($favourites) { + $favouritecourseids = array_flip(array_map( + function($favourite) { + return $favourite->itemid; + }, $favourites)); + } + } + if(!empty($months)) + { + $sixmonthsago = strtotime("-{$months} months"); // Calculate 6 months ago + } + + foreach ($courses as $course) { + + $enrolrecords = $DB->get_records_sql(" + SELECT ue.timestart,ue.timeend + FROM {user_enrolments} ue + JOIN {enrol} e ON ue.enrolid = e.id + WHERE ue.userid = :userid AND e.courseid = :courseid + ORDER BY ue.timestart DESC + ", [ + 'userid' => $userid, + 'courseid' => $course->id, + ]); + + // Get the first record's timestart (most recent) + $enroltime = 0; + if (!empty($enrolrecords)) { + $firstrecord = reset($enrolrecords); // Gets first item + $enroltime = (int)$firstrecord->timestart; + $enrolendtime=(int)$firstrecord->timeend; + } + if(!empty($months)) + { + if ($enroltime && (int)$enroltime < $sixmonthsago) { + continue; + } + } + + $context = context_course::instance($course->id, IGNORE_MISSING); + try { + self::validate_context($context); + } catch (Exception $e) { + // current user can not access this course, sorry we can not disclose who is enrolled in this course! + continue; + } + + // If viewing details of another user, then we must be able to view participants as well as profile of that user. + if (!$sameuser && (!course_can_view_participants($context) || !user_can_view_profile($user, $course))) { + continue; + } + + if ($returnusercount) { + list($enrolledsqlselect, $enrolledparams) = get_enrolled_sql($context); + $enrolledsql = "SELECT COUNT('x') FROM ($enrolledsqlselect) enrolleduserids"; + $enrolledusercount = $DB->count_records_sql($enrolledsql, $enrolledparams); + } + + $displayname = \core_external\util::format_string(get_course_display_name_for_list($course), $context); + list($course->summary, $course->summaryformat) = + \core_external\util::format_text($course->summary, $course->summaryformat, $context, 'course', 'summary', null); + $course->fullname = \core_external\util::format_string($course->fullname, $context); + $course->shortname = \core_external\util::format_string($course->shortname, $context); + + + $progress = null; + $completed = null; + $completionhascriteria = false; + $completionusertracked = false; + + // Return only private information if the user should be able to see it. + if ($sameuser || completion_can_view_data($userid, $course)) { + if ($course->enablecompletion) { + $completion = new completion_info($course); + $completed = $completion->is_course_complete($userid); + $completionhascriteria = $completion->has_criteria(); + $completionusertracked = $completion->is_tracked_user($userid); + $progress = \core_completion\progress::get_course_progress_percentage($course, $userid); + + $activities = $completion->get_activities(); + $totalactivities = count($activities); + $completedactivities = 0; + foreach ($activities as $cm) { + $data = $completion->get_data($cm, false, $userid); + if ($data->completionstate == COMPLETION_COMPLETE || $data->completionstate == COMPLETION_COMPLETE_PASS) { + $completedactivities++; + } + } + } + } + + $lastaccess = null; + // Check if last access is a hidden field. + $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields)); + $canviewlastaccess = $sameuser || !isset($hiddenfields['lastaccess']); + if (!$canviewlastaccess) { + $canviewlastaccess = has_capability('moodle/course:viewhiddenuserfields', $context); + } + + if ($canviewlastaccess && isset($user->lastcourseaccess[$course->id])) { + $lastaccess = $user->lastcourseaccess[$course->id]; + } + + $hidden = false; + if ($sameuser) { + $hidden = boolval(get_user_preferences('block_myoverview_hidden_course_' . $course->id, 0)); + } + + // Retrieve course overview used files. + $courselist = new core_course_list_element($course); + $overviewfiles = array(); + foreach ($courselist->get_course_overviewfiles() as $file) { + $fileurl = moodle_url::make_webservice_pluginfile_url($file->get_contextid(), $file->get_component(), + $file->get_filearea(), null, $file->get_filepath(), + $file->get_filename())->out(false); + $overviewfiles[] = array( + 'filename' => $file->get_filename(), + 'fileurl' => $fileurl, + 'filesize' => $file->get_filesize(), + 'filepath' => $file->get_filepath(), + 'mimetype' => $file->get_mimetype(), + 'timemodified' => $file->get_timemodified(), + ); + } + + $courseimage = \core_course\external\course_summary_exporter::get_course_image($course); + if (!$courseimage) { + $courseimage = $OUTPUT->get_generated_url_for_course($context); + } + $hascertificate = $DB->record_exists('customcert', ['course' => $course->id]); + $courseresult = [ + 'id' => $course->id, + 'shortname' => $course->shortname, + 'fullname' => $course->fullname, + 'displayname' => $displayname, + 'idnumber' => $course->idnumber, + 'visible' => $course->visible, + 'summary' => $course->summary, + 'summaryformat' => $course->summaryformat, + 'format' => $course->format, + 'courseimage' => $courseimage, + 'showgrades' => $course->showgrades, + 'lang' => clean_param($course->lang, PARAM_LANG), + 'enablecompletion' => $course->enablecompletion, + 'completionhascriteria' => $completionhascriteria, + 'completionusertracked' => $completionusertracked, + 'category' => $course->category, + 'progress' => $progress, + 'completed' => $completed, + 'startdate' => $enroltime, + 'enddate' => $enrolendtime, + 'marker' => $course->marker, + 'lastaccess' => $lastaccess, + 'isfavourite' => isset($favouritecourseids[$course->id]), + 'hidden' => $hidden, + 'overviewfiles' => $overviewfiles, + 'showactivitydates' => $course->showactivitydates, + 'showcompletionconditions' => $course->showcompletionconditions, + 'timemodified' => $course->timemodified, + 'certificateenabled'=>$hascertificate, + 'totalactivities' => $totalactivities, + 'completedactivities'=>$completedactivities + + ]; + if ($returnusercount) { + $courseresult['enrolledusercount'] = $enrolledusercount; + } + $result[] = $courseresult; + } + + return $result; + } + + public static function get_recent_courses_returns() { + return new external_multiple_structure( + new external_single_structure( + array( + 'id' => new external_value(PARAM_INT, 'id of course'), + 'shortname' => new external_value(PARAM_RAW, 'short name of course'), + 'fullname' => new external_value(PARAM_RAW, 'long name of course'), + 'displayname' => new external_value(PARAM_RAW, 'course display name for lists.', VALUE_OPTIONAL), + 'enrolledusercount' => new external_value(PARAM_INT, 'Number of enrolled users in this course', + VALUE_OPTIONAL), + 'idnumber' => new external_value(PARAM_RAW, 'id number of course'), + 'visible' => new external_value(PARAM_INT, '1 means visible, 0 means not yet visible course'), + 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL), + 'summaryformat' => new external_format_value('summary', VALUE_OPTIONAL), + 'format' => new external_value(PARAM_PLUGIN, 'course format: weeks, topics, social, site', VALUE_OPTIONAL), + 'courseimage' => new external_value(PARAM_URL, 'The course image URL', VALUE_OPTIONAL), + 'showgrades' => new external_value(PARAM_BOOL, 'true if grades are shown, otherwise false', VALUE_OPTIONAL), + 'lang' => new external_value(PARAM_LANG, 'forced course language', VALUE_OPTIONAL), + 'enablecompletion' => new external_value(PARAM_BOOL, 'true if completion is enabled, otherwise false', + VALUE_OPTIONAL), + 'completionhascriteria' => new external_value(PARAM_BOOL, 'If completion criteria is set.', VALUE_OPTIONAL), + 'completionusertracked' => new external_value(PARAM_BOOL, 'If the user is completion tracked.', VALUE_OPTIONAL), + 'category' => new external_value(PARAM_INT, 'course category id', VALUE_OPTIONAL), + 'progress' => new external_value(PARAM_FLOAT, 'Progress percentage', VALUE_OPTIONAL), + 'completed' => new external_value(PARAM_BOOL, 'Whether the course is completed.', VALUE_OPTIONAL), + 'startdate' => new external_value(PARAM_INT, 'Timestamp when the course start', VALUE_OPTIONAL), + 'enddate' => new external_value(PARAM_INT, 'Timestamp when the course end', VALUE_OPTIONAL), + 'marker' => new external_value(PARAM_INT, 'Course section marker.', VALUE_OPTIONAL), + 'lastaccess' => new external_value(PARAM_INT, 'Last access to the course (timestamp).', VALUE_OPTIONAL), + 'isfavourite' => new external_value(PARAM_BOOL, 'If the user marked this course a favourite.', VALUE_OPTIONAL), + 'hidden' => new external_value(PARAM_BOOL, 'If the user hide the course from the dashboard.', VALUE_OPTIONAL), + 'overviewfiles' => new external_files('Overview files attached to this course.', VALUE_OPTIONAL), + 'showactivitydates' => new external_value(PARAM_BOOL, 'Whether the activity dates are shown or not'), + 'showcompletionconditions' => new external_value(PARAM_BOOL, 'Whether the activity completion conditions are shown or not'), + 'timemodified' => new external_value(PARAM_INT, 'Last time course settings were updated (timestamp).', + VALUE_OPTIONAL), + 'certificateenabled' => new external_value(PARAM_BOOL, 'Whether the course has certificate.', VALUE_OPTIONAL), + 'totalactivities' => new external_value(PARAM_INT, 'total activities', VALUE_OPTIONAL), + 'completedactivities' => new external_value(PARAM_INT, 'completed activities count', VALUE_OPTIONAL), + ) + ) + ); + } +} diff --git a/local/mylearningservice/lang/en/local_mylearningservice.php b/local/mylearningservice/lang/en/local_mylearningservice.php new file mode 100644 index 00000000000..af9370d6f07 --- /dev/null +++ b/local/mylearningservice/lang/en/local_mylearningservice.php @@ -0,0 +1,2 @@ +component = 'local_mylearningservice'; +$plugin->version = 2025073100; +$plugin->requires = 2024100290; +$plugin->maturity = MATURITY_STABLE; +$plugin->release = '1.0';