ÿØÿà JFIF    ÿÛ „  ( %!1"%)+...383-7(-.+  -%%--------+------------/----------------------------ÿÀ  ¶" ÿÄ     ÿÄ @    !1AQaq‘¡"±ÁÑð2’BRráS‚ñ3b¢CT²Ò #cÿÄ    ÿÄ '   1!AQ2a"‘±#ÿÚ   ? ò(/p‘—–¢·,M|¯G»R£üešT. ôŸEoCFñ‡ 4ÔkåóxR|µÁ¨ÓDà!Ç´µ˜‡p ÍQ‹§!þž^õWsŒy  åÁ§$á«%ºq˜™JyyË0y«^Ϗ -3ÃÏËÈ£Æ\P’ä„s&•‹Üs üô'àWt¹pñCp$bÚF`·7.^)¾!)ÒÙ˜–ÅÏš^h¾ø³·ìe±ŒaeÖ—Í,e=B}Jéߦ$$½—šÙÜCC¯¶è8β–Åkèwx LÉngT±æ¹£Ócœ¿Í/©Éÿ ‰ÚCÒ­mq;Tt¶–}0"zÖPcd1š­¤tˆ„ÐÓâqú[™öUCŠ2޿د­É(¸ßúçšmW,Ò ºf‹Ån™ÄLêÁ8üÁkÁ€L¥ÍuJ1œôrÂSŒÓöt!7EUíïb)AËi¬â-ÙHk8òÁiXYwÅ)O^"ÇØriÛô{»wRUKÙÏÚlÓ¬€‚’שT ] ªb—ÊîKC^×M“xùäñz¬zJ× {4[¹M'IÆeH1LCWÕ]œûº WDæà°OÅ‚¹c²ÞuA–rL`HšpCez‡V’— Ž‚Ë„–ñ­'µR0ÕÖ=À:±G»C\nÆÉ5*¢ †3šhQ4ýÒi$Õ1"Ü]¢p-q3zcQ$ªö¨¥æf›‘»´ÝÚHá„e²JÇ–iÊ:·à¬Øc5Åk»Laª*wi [0Ówk ¦a¦0Õ²ÅÅ‚T¸’³q:Æ<¹ú Ò˜Æ}2VE•Ѐp¡Ô3Úº …h³^LJºçì¥Ánó|˜ÖˆÎˆ+« ¦dø‘Mº–d\ªDðÛ„Ì×LÝ——Ð9Ç|•FèçVm™¨€™3õS–9Äå¢<̉Ìm¢±`·º˜&Z²ž Ë®D«¶»6™‰©­HÈ Àá«h­i³04æ’hxœi3ï¨(ÓL·†‹¶ËpŒ ƒÜÒ? :õdsÀ –3æÓ·Üjâ¬9—]8n¼&]I F£¼…ì–—Œ0v°LǐÅfÛ2TÖ’aµ˜N@ÖBSÏ^à´4-±°b×nÈÈœÆD=KdŒø…ÄÈK,›ÖR§fjü$^ÔÎ!„6^ s:Üun\ôkCÞò÷<—Lúl Í›G¹ÀPá¼Nc¡šÒ…ÙWͳœ±vÊáЄ×9‹P‰cEØ‘™›µŸP<×g1 º©Ø¬wiÁ^l5× I/';Ë+´Z‡ÓÄFÕp[Èð’ HÄ ó 9¬Dl4“é±Ï”V=^Hð;žLädÉa‚°Ë,#/Ž~²BÔÛ QãñQtIeórVB,ÏÂf‹4#Μc‹É3ZIÖ Œ¢¥·üxa‡Ãçè„jŠ[5 Ä!U?,3žÎ×…ðdÓ÷háŠA‰ìÄ4÷ÃÜZÃE{‰®+W\ZÍE[‰»µk»MÝ­`¢©†˜ÃV‹-f¢¡†¢a«eŠ%«Y¨©q%jâd,Ôq`) ¤¦…šˆ ¦E>0Ùb²t–Ÿ  ÷jsςҒKÈcø(é›;k(’#ôŒQ ™C> õ³M9óºÆµ¤çâ;uÀ)5>RÔ¸fÓ~Ȧ—’-$fe«"FâUˆÊwµPÏu w+"åÉó¨6s™Û9(À¨ù©hE°½†n`m~“@©›p!Vkhµ Á1šbEqqÊ@ºèl›2 –Yã%‘‚hÛ{a¶‡ÅÞM²¬çL34®Çl]®‹ŽØŒm'RÜe35æöˆ­Á­ cP9šeÁköo´L³Ì=p8]”‡B¶,šº|ɏdzXˆØk*ÇÚ{#ñ‰pã(€¶‡9ý'šÜ³½¯kƒ†Â’ëSOƒ•Å®H6+a¢µˆ‹Y¨b˜b+XˆÖ!°u!¢"µˆˆn2ˆ Å0Ä`Å Ô®c(ÓÜGº•Ä»Œ âWî%q.áÐÄ÷î%umÍ£+ÜQ,Vn¦-GphU¸¢X­&,Gt ·K¢ÅÅ·¥RÄÊÉbKn N­R2g$VµÕó Qr#–ÓZUÏð0ÎtÙÀœz ëöo“Îk¯n„T°"SÞ+³/…€½"ižãO%Í%&Δâ‘ËØ´IˆÓ°Ë‰ÞM3.™yçÁzit›`4C`k¢¼á!álÄï sÂ[×!¤l¾æo"ƒIÔ'@ªu)3›ô®“@Z à¾] üœ'‡4¸PÞkÁ-qÄH‰ÓÍBd´ÿ —¹°ÄA&ÊUC„öz‚‚RæŠtf¬>˜ÑidšEçÑL÷áý¡V±h§­c¿¥¨ƒ@g¼…Ýö^ÞËc.ž„æ“I[ ª+·zÐÒÚ,½Ð»¶€C›7jk$๖€Ž¾ÅÙpp¶ÍþéΤ ½ØÎŒq ñpæžÅ¥#Øâ49ÃÂK^MYV“,¶Éz_òæ:…;Õ«¦I3âJ£¤û5$7Èxün\â]Êe¯(Ÿ&Ž‹¶2<6Ćf× æ¢ƱqWı[(ÙÂŽç‚T7¨@ÈÉÍß‚ïÙ"&3TYlGށ†)†#5ŠA©^T2ÆÁ†)©†©†¤yGXÁ†©"†)©¼ÅV WS†#©]R–r‹q5ÕbâWþBh¯u+ªÅÄ×ü„nÑ^êkªÁj‰jeœWˆÕÕ`µDµ2Ì+Ä µDµµ5Ô{¤Þ0RF’H÷AÛ<ÊÁ§ ƈæ0Ñ£ê4 öÚµ[¬/1²€â<-¬šö²r×0o×w ´xÐÒË„ g01¬„åEM蛇Ã\6µE›‘fÙO k÷CºnÓ1`iîÒ² áÂ!ÑLÄÿ ³¬\õ*:M=Ç»c‹2[‰ØsÕLò\ŒK L^™üÄN\¶#» Ç\……ßïIœÉ$¸ÎñÍ®ÔHÇ Ûí/´>Li”„†g^$ 7NÍÑÑ2M2ªNg=k½Ð}Ÿl6‡ —JWˆ”ÞéT·+µ¦üÂFè¢Vs6ýÆ4»2+2IÝ9ô9®ÇGÙÇ1+Ä8;*Õ§ž½D*îÑWÚçá3Ma¤·ˆ¥t}š²Ü…Q+Æüµ8HíS”üxGéÁˆ¦Ël„éÈ 5 µÄƒ6äÜ(kIš¯S†fÖ&¸®×v|Äyˆ*禌Éù@7-®Ä[]ÎÖ¿ê`º…=:…žJVnݳvðÖKE¼‚ »mà ÷f®:Ë ™–4rð¡4` ܹçž~‹C=œ…¾Ïj|VDtK.ƒ"9 Ï ­–éXá³6WÒïIL-Ї Ç home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/notes/externallib.php000064400000071366152266551430023061 0ustar00. use core_external\external_api; use core_external\external_format_value; use core_external\external_function_parameters; use core_external\external_multiple_structure; use core_external\external_single_structure; use core_external\external_value; use core_external\external_warnings; use core_external\util; /** * External notes API * * @package core_notes * @category external * @copyright 2011 Jerome Mouneyrac * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); require_once($CFG->dirroot . "/notes/lib.php"); /** * Notes external functions * * @package core_notes * @category external * @copyright 2011 Jerome Mouneyrac * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.2 */ class core_notes_external extends external_api { /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.2 */ public static function create_notes_parameters() { return new external_function_parameters( array( 'notes' => new external_multiple_structure( new external_single_structure( array( 'userid' => new external_value(PARAM_INT, 'id of the user the note is about'), 'publishstate' => new external_value(PARAM_ALPHA, '\'personal\', \'course\' or \'site\''), 'courseid' => new external_value(PARAM_INT, 'course id of the note (in Moodle a note can only be created into a course, even for site and personal notes)'), 'text' => new external_value(PARAM_RAW, 'the text of the message - text or HTML'), 'format' => new external_format_value('text', VALUE_DEFAULT, FORMAT_MOODLE), 'clientnoteid' => new external_value(PARAM_ALPHANUMEXT, 'your own client id for the note. If this id is provided, the fail message id will be returned to you', VALUE_OPTIONAL), ) ) ) ) ); } /** * Create notes about some users * Note: code should be matching the /notes/edit.php checks * and the /user/addnote.php checks. (they are similar cheks) * * @param array $notes An array of notes to create. * @return array (success infos and fail infos) * @since Moodle 2.2 */ public static function create_notes($notes = array()) { global $CFG, $DB; $params = self::validate_parameters(self::create_notes_parameters(), array('notes' => $notes)); // Check if note system is enabled. if (!$CFG->enablenotes) { throw new moodle_exception('notesdisabled', 'notes'); } // Retrieve all courses. $courseids = array(); foreach ($params['notes'] as $note) { $courseids[] = $note['courseid']; } $courses = $DB->get_records_list("course", "id", $courseids); // Retrieve all users of the notes. $userids = array(); foreach ($params['notes'] as $note) { $userids[] = $note['userid']; } list($sqluserids, $sqlparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED, 'userid_'); $users = $DB->get_records_select("user", "id " . $sqluserids . " AND deleted = 0", $sqlparams); $resultnotes = array(); foreach ($params['notes'] as $note) { $success = true; $resultnote = array(); // The infos about the success of the operation. // Check the course exists. if (empty($courses[$note['courseid']])) { $success = false; $errormessage = get_string('invalidcourseid', 'error'); } else { // Ensure the current user is allowed to run this function. $context = context_course::instance($note['courseid']); self::validate_context($context); require_capability('moodle/notes:manage', $context); } // Check the user exists. if (empty($users[$note['userid']])) { $success = false; $errormessage = get_string('invaliduserid', 'notes', $note['userid']); } // Build the resultnote. if (isset($note['clientnoteid'])) { $resultnote['clientnoteid'] = $note['clientnoteid']; } if ($success) { // Now we can create the note. $dbnote = new stdClass; $dbnote->courseid = $note['courseid']; $dbnote->userid = $note['userid']; // Need to support 'html' and 'text' format values for backward compatibility. switch (strtolower($note['format'])) { case 'html': $textformat = FORMAT_HTML; break; case 'text': $textformat = FORMAT_PLAIN; break; default: $textformat = util::validate_format($note['format']); break; } $dbnote->content = $note['text']; $dbnote->format = $textformat; // Get the state ('personal', 'course', 'site'). switch ($note['publishstate']) { case 'personal': $dbnote->publishstate = NOTES_STATE_DRAFT; break; case 'course': $dbnote->publishstate = NOTES_STATE_PUBLIC; break; case 'site': $dbnote->publishstate = NOTES_STATE_SITE; $dbnote->courseid = SITEID; break; default: break; } // TODO MDL-31119 performance improvement - if possible create a bulk functions for saving multiple notes at once if (note_save($dbnote)) { // Note_save attribut an id in case of success. $success = $dbnote->id; } $resultnote['noteid'] = $success; } else { // WARNINGS: for backward compatibility we return this errormessage. // We should have thrown exceptions as these errors prevent results to be returned. // See http://docs.moodle.org/dev/Errors_handling_in_web_services#When_to_send_a_warning_on_the_server_side . $resultnote['noteid'] = -1; $resultnote['errormessage'] = $errormessage; } $resultnotes[] = $resultnote; } return $resultnotes; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.2 */ public static function create_notes_returns() { return new external_multiple_structure( new external_single_structure( array( 'clientnoteid' => new external_value(PARAM_ALPHANUMEXT, 'your own id for the note', VALUE_OPTIONAL), 'noteid' => new external_value(PARAM_INT, 'ID of the created note when successful, -1 when failed'), 'errormessage' => new external_value(PARAM_TEXT, 'error message - if failed', VALUE_OPTIONAL) ) ) ); } /** * Returns description of delete_notes parameters * * @return external_function_parameters * @since Moodle 2.5 */ public static function delete_notes_parameters() { return new external_function_parameters( array( "notes"=> new external_multiple_structure( new external_value(PARAM_INT, 'ID of the note to be deleted'), 'Array of Note Ids to be deleted.' ) ) ); } /** * Delete notes about users. * Note: code should be matching the /notes/delete.php checks. * * @param array $notes An array of ids for the notes to delete. * @return null * @since Moodle 2.5 */ public static function delete_notes($notes = array()) { global $CFG; $params = self::validate_parameters(self::delete_notes_parameters(), array('notes' => $notes)); // Check if note system is enabled. if (!$CFG->enablenotes) { throw new moodle_exception('notesdisabled', 'notes'); } $warnings = array(); foreach ($params['notes'] as $noteid) { $note = note_load($noteid); if (isset($note->id)) { // Ensure the current user is allowed to run this function. $context = context_course::instance($note->courseid); self::validate_context($context); require_capability('moodle/notes:manage', $context); note_delete($note); } else { $warnings[] = array('item'=>'note', 'itemid'=>$noteid, 'warningcode'=>'badid', 'message'=>'Note does not exist'); } } return $warnings; } /** * Returns description of delete_notes result value. * * @return \core_external\external_description * @since Moodle 2.5 */ public static function delete_notes_returns() { return new external_warnings('item is always \'note\'', 'When errorcode is savedfailed the note could not be modified.' . 'When errorcode is badparam, an incorrect parameter was provided.' . 'When errorcode is badid, the note does not exist', 'errorcode can be badparam (incorrect parameter), savedfailed (could not be modified), or badid (note does not exist)'); } /** * Returns description of get_notes parameters. * * @return external_function_parameters * @since Moodle 2.5 */ public static function get_notes_parameters() { return new external_function_parameters( array( "notes"=> new external_multiple_structure( new external_value(PARAM_INT, 'ID of the note to be retrieved'), 'Array of Note Ids to be retrieved.' ) ) ); } /** * Get notes about users. * * @param array $notes An array of ids for the notes to retrieve. * @return null * @since Moodle 2.5 */ public static function get_notes($notes) { global $CFG; $params = self::validate_parameters(self::get_notes_parameters(), array('notes' => $notes)); // Check if note system is enabled. if (!$CFG->enablenotes) { throw new moodle_exception('notesdisabled', 'notes'); } $resultnotes = array(); foreach ($params['notes'] as $noteid) { $resultnote = array(); $note = note_load($noteid); if (isset($note->id)) { // Ensure the current user is allowed to run this function. $context = context_course::instance($note->courseid); self::validate_context($context); require_capability('moodle/notes:view', $context); list($gotnote['text'], $gotnote['format']) = util::format_text($note->content, $note->format, $context->id, 'notes', '', ''); $gotnote['noteid'] = $note->id; $gotnote['userid'] = $note->userid; $gotnote['publishstate'] = $note->publishstate; $gotnote['courseid'] = $note->courseid; $resultnotes["notes"][] = $gotnote; } else { $resultnotes["warnings"][] = array('item' => 'note', 'itemid' => $noteid, 'warningcode' => 'badid', 'message' => 'Note does not exist'); } } return $resultnotes; } /** * Returns description of get_notes result value. * * @return \core_external\external_description * @since Moodle 2.5 */ public static function get_notes_returns() { return new external_single_structure( array( 'notes' => new external_multiple_structure( new external_single_structure( array( 'noteid' => new external_value(PARAM_INT, 'id of the note', VALUE_OPTIONAL), 'userid' => new external_value(PARAM_INT, 'id of the user the note is about', VALUE_OPTIONAL), 'publishstate' => new external_value(PARAM_ALPHA, '\'personal\', \'course\' or \'site\'', VALUE_OPTIONAL), 'courseid' => new external_value(PARAM_INT, 'course id of the note', VALUE_OPTIONAL), 'text' => new external_value(PARAM_RAW, 'the text of the message - text or HTML', VALUE_OPTIONAL), 'format' => new external_format_value('text', VALUE_OPTIONAL), ), 'note' ) ), 'warnings' => new external_warnings('item is always \'note\'', 'When errorcode is savedfailed the note could not be modified.' . 'When errorcode is badparam, an incorrect parameter was provided.' . 'When errorcode is badid, the note does not exist', 'errorcode can be badparam (incorrect parameter), savedfailed (could not be modified), or badid (note does not exist)') ) ); } /** * Returns description of update_notes parameters. * * @return external_function_parameters * @since Moodle 2.5 */ public static function update_notes_parameters() { return new external_function_parameters( array( 'notes' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'id of the note'), 'publishstate' => new external_value(PARAM_ALPHA, '\'personal\', \'course\' or \'site\''), 'text' => new external_value(PARAM_RAW, 'the text of the message - text or HTML'), 'format' => new external_format_value('text', VALUE_DEFAULT), ) ), "Array of Notes", VALUE_DEFAULT, array() ) ) ); } /** * Update notes about users. * * @param array $notes An array of ids for the notes to update. * @return array fail infos. * @since Moodle 2.2 */ public static function update_notes($notes = array()) { global $CFG, $DB; $params = self::validate_parameters(self::update_notes_parameters(), array('notes' => $notes)); // Check if note system is enabled. if (!$CFG->enablenotes) { throw new moodle_exception('notesdisabled', 'notes'); } $warnings = array(); foreach ($params['notes'] as $note) { $notedetails = note_load($note['id']); if (isset($notedetails->id)) { // Ensure the current user is allowed to run this function. $context = context_course::instance($notedetails->courseid); self::validate_context($context); require_capability('moodle/notes:manage', $context); $dbnote = new stdClass; $dbnote->id = $note['id']; $dbnote->content = $note['text']; $dbnote->format = util::validate_format($note['format']); // Get the state ('personal', 'course', 'site'). switch ($note['publishstate']) { case 'personal': $dbnote->publishstate = NOTES_STATE_DRAFT; break; case 'course': $dbnote->publishstate = NOTES_STATE_PUBLIC; break; case 'site': $dbnote->publishstate = NOTES_STATE_SITE; $dbnote->courseid = SITEID; break; default: $warnings[] = array('item' => 'note', 'itemid' => $note["id"], 'warningcode' => 'badparam', 'message' => 'Provided publishstate incorrect'); break; } if (!note_save($dbnote)) { $warnings[] = array('item' => 'note', 'itemid' => $note["id"], 'warningcode' => 'savedfailed', 'message' => 'Note could not be modified'); } } else { $warnings[] = array('item' => 'note', 'itemid' => $note["id"], 'warningcode' => 'badid', 'message' => 'Note does not exist'); } } return $warnings; } /** * Returns description of update_notes result value. * * @return \core_external\external_description * @since Moodle 2.5 */ public static function update_notes_returns() { return new external_warnings('item is always \'note\'', 'When errorcode is savedfailed the note could not be modified.' . 'When errorcode is badparam, an incorrect parameter was provided.' . 'When errorcode is badid, the note does not exist', 'errorcode can be badparam (incorrect parameter), savedfailed (could not be modified), or badid (note does not exist)'); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.9 */ public static function get_course_notes_parameters() { return new external_function_parameters( array( 'courseid' => new external_value(PARAM_INT, 'course id, 0 for SITE'), 'userid' => new external_value(PARAM_INT, 'user id', VALUE_DEFAULT, 0), ) ); } /** * Create a notes list * * @param int $courseid ID of the Course * @param stdClass $context context object * @param int $userid ID of the User * @param int $state * @param int $author * @return array of notes * @since Moodle 2.9 */ protected static function create_note_list($courseid, $context, $userid, $state, $author = 0) { $results = []; $notes = note_list($courseid, $userid, $state, $author); foreach ($notes as $key => $note) { $note = (array)$note; [$note['content'], $note['format']] = util::format_text( $note['content'], $note['format'], $context->id, '', '', 0 ); $results[$key] = $note; } return $results; } /** * Get a list of course notes * * @param int $courseid ID of the Course * @param int $userid ID of the User * @return array of site, course and personal notes and warnings * @since Moodle 2.9 * @throws moodle_exception */ public static function get_course_notes($courseid, $userid = 0) { global $CFG, $USER; if (empty($CFG->enablenotes)) { throw new moodle_exception('notesdisabled', 'notes'); } $warnings = array(); $arrayparams = array( 'courseid' => $courseid, 'userid' => $userid, ); $params = self::validate_parameters(self::get_course_notes_parameters(), $arrayparams); if (empty($params['courseid'])) { $params['courseid'] = SITEID; } $user = null; if (!empty($params['userid'])) { $user = core_user::get_user($params['userid'], '*', MUST_EXIST); core_user::require_active_user($user); } $course = get_course($params['courseid']); $systemcontext = context_system::instance(); $canmanagesystemnotes = has_capability('moodle/notes:manage', $systemcontext); if ($course->id == SITEID) { $context = $systemcontext; $canmanagecoursenotes = $canmanagesystemnotes; } else { $context = context_course::instance($course->id); $canmanagecoursenotes = has_capability('moodle/notes:manage', $context); } self::validate_context($context); $sitenotes = array(); $coursenotes = array(); $personalnotes = array(); if ($course->id != SITEID) { require_capability('moodle/notes:view', $context); $sitenotes = self::create_note_list(0, $systemcontext, $params['userid'], NOTES_STATE_SITE); $coursenotes = self::create_note_list($course->id, $context, $params['userid'], NOTES_STATE_PUBLIC); $personalnotes = self::create_note_list($course->id, $context, $params['userid'], NOTES_STATE_DRAFT, $USER->id); } else { if (has_capability('moodle/notes:view', $context)) { $sitenotes = self::create_note_list(0, $context, $params['userid'], NOTES_STATE_SITE); } // It returns notes only for a specific user! if (!empty($user)) { $usercourses = enrol_get_users_courses($user->id, true); foreach ($usercourses as $c) { // All notes at course level, only if we have capability on every course. if (has_capability('moodle/notes:view', context_course::instance($c->id))) { $coursenotes += self::create_note_list($c->id, $context, $params['userid'], NOTES_STATE_PUBLIC); } } } } $results = array( 'sitenotes' => $sitenotes, 'coursenotes' => $coursenotes, 'personalnotes' => $personalnotes, 'canmanagesystemnotes' => $canmanagesystemnotes, 'canmanagecoursenotes' => $canmanagecoursenotes, 'warnings' => $warnings ); return $results; } /** * Returns array of note structure * * @return \core_external\external_description * @since Moodle 2.9 */ protected static function get_note_structure() { return array( 'id' => new external_value(PARAM_INT, 'id of this note'), 'courseid' => new external_value(PARAM_INT, 'id of the course'), 'userid' => new external_value(PARAM_INT, 'user id'), 'content' => new external_value(PARAM_RAW, 'the content text formated'), 'format' => new external_format_value('content'), 'created' => new external_value(PARAM_INT, 'time created (timestamp)'), 'lastmodified' => new external_value(PARAM_INT, 'time of last modification (timestamp)'), 'usermodified' => new external_value(PARAM_INT, 'user id of the creator of this note'), 'publishstate' => new external_value(PARAM_ALPHA, "state of the note (i.e. draft, public, site) ") ); } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.9 */ public static function get_course_notes_returns() { return new external_single_structure( array( 'sitenotes' => new external_multiple_structure( new external_single_structure(self::get_note_structure() , ''), 'site notes', VALUE_OPTIONAL ), 'coursenotes' => new external_multiple_structure( new external_single_structure(self::get_note_structure() , ''), 'couse notes', VALUE_OPTIONAL ), 'personalnotes' => new external_multiple_structure( new external_single_structure(self::get_note_structure() , ''), 'personal notes', VALUE_OPTIONAL ), 'canmanagesystemnotes' => new external_value(PARAM_BOOL, 'Whether the user can manage notes at system level.', VALUE_OPTIONAL), 'canmanagecoursenotes' => new external_value(PARAM_BOOL, 'Whether the user can manage notes at the given course.', VALUE_OPTIONAL), 'warnings' => new external_warnings() ), 'notes' ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.9 */ public static function view_notes_parameters() { return new external_function_parameters( array( 'courseid' => new external_value(PARAM_INT, 'course id, 0 for notes at system level'), 'userid' => new external_value(PARAM_INT, 'user id, 0 means view all the user notes', VALUE_DEFAULT, 0) ) ); } /** * Simulates the web interface view of notes/index.php: trigger events * * @param int $courseid id of the course * @param int $userid id of the user * @return array of warnings and status result * @since Moodle 2.9 * @throws moodle_exception */ public static function view_notes($courseid, $userid = 0) { global $CFG; require_once($CFG->dirroot . "/notes/lib.php"); if (empty($CFG->enablenotes)) { throw new moodle_exception('notesdisabled', 'notes'); } $warnings = array(); $arrayparams = array( 'courseid' => $courseid, 'userid' => $userid ); $params = self::validate_parameters(self::view_notes_parameters(), $arrayparams); if (empty($params['courseid'])) { $params['courseid'] = SITEID; } $course = get_course($params['courseid']); if ($course->id == SITEID) { $context = context_system::instance(); } else { $context = context_course::instance($course->id); } // First of all, validate the context before do further permission checks. self::validate_context($context); require_capability('moodle/notes:view', $context); if (!empty($params['userid'])) { $user = core_user::get_user($params['userid'], '*', MUST_EXIST); core_user::require_active_user($user); if ($course->id != SITEID and !can_access_course($course, $user, '', true)) { throw new moodle_exception('notenrolledprofile'); } } note_view($context, $params['userid']); $result = array(); $result['status'] = true; $result['warnings'] = $warnings; return $result; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.9 */ public static function view_notes_returns() { return new external_single_structure( array( 'status' => new external_value(PARAM_BOOL, 'status: true if success'), 'warnings' => new external_warnings() ) ); } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/webservice/externallib.php000064400000036551152266552440024066 0ustar00. use core_external\external_function_parameters; use core_external\external_multiple_structure; use core_external\external_single_structure; use core_external\external_value; /** * Web service related functions * * @package core_webservice * @category external * @copyright 2011 Jerome Mouneyrac * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.2 */ class core_webservice_external extends \core_external\external_api { /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.2 */ public static function get_site_info_parameters() { return new external_function_parameters( array('serviceshortnames' => new external_multiple_structure ( new external_value( PARAM_ALPHANUMEXT, 'service shortname'), 'DEPRECATED PARAMETER - it was a design error in the original implementation. \ It is ignored now. (parameter kept for backward compatibility)', VALUE_DEFAULT, array() ), ) ); } /** * Return user information including profile picture + basic site information * Note: * - no capability checking because we return only known information about logged user * * @param array $serviceshortnames - DEPRECATED PARAMETER - values will be ignored - * it was an original design error, we keep for backward compatibility. * @return array site info * @since Moodle 2.2 */ public static function get_site_info($serviceshortnames = array()) { global $USER, $SITE, $CFG, $DB, $PAGE; $params = self::validate_parameters(self::get_site_info_parameters(), array('serviceshortnames'=>$serviceshortnames)); $context = context_user::instance($USER->id); $systemcontext = context_system::instance(); $userpicture = new user_picture($USER); $userpicture->size = 1; // Size f1. $profileimageurl = $userpicture->get_url($PAGE); // Site information. $siteinfo = array( 'sitename' => \core_external\util::format_string($SITE->fullname, $systemcontext), 'siteurl' => $CFG->wwwroot, 'username' => $USER->username, 'firstname' => $USER->firstname, 'lastname' => $USER->lastname, 'fullname' => fullname($USER), 'lang' => clean_param(current_language(), PARAM_LANG), 'userid' => $USER->id, 'userpictureurl' => $profileimageurl->out(false), 'siteid' => SITEID ); // Retrieve the service and functions from the web service linked to the token // If you call this function directly from external (not a web service call), // then it will still return site info without information about a service // Note: wsusername/wspassword ws authentication is not supported. $functions = array(); if ($CFG->enablewebservices) { // No need to check token if web service are disabled and not a ws call. $token = optional_param('wstoken', '', PARAM_ALPHANUM); if (!empty($token)) { // No need to run if not a ws call. // Retrieve service shortname. $servicesql = 'SELECT s.* FROM {external_services} s, {external_tokens} t WHERE t.externalserviceid = s.id AND token = ? AND t.userid = ? AND s.enabled = 1'; $service = $DB->get_record_sql($servicesql, array($token, $USER->id)); $siteinfo['downloadfiles'] = $service->downloadfiles; $siteinfo['uploadfiles'] = $service->uploadfiles; if (!empty($service)) { // Return the release and version number for web service users only. $siteinfo['release'] = $CFG->release; $siteinfo['version'] = $CFG->version; // Retrieve the functions. $functionssql = "SELECT f.* FROM {external_functions} f, {external_services_functions} sf WHERE f.name = sf.functionname AND sf.externalserviceid = ?"; $functions = $DB->get_records_sql($functionssql, array($service->id)); } else { throw new coding_exception('No service found in get_site_info: something is buggy, \ it should have fail at the ws server authentication layer.'); } } } // Build up the returned values of the list of functions. $componentversions = array(); $availablefunctions = array(); foreach ($functions as $function) { $functioninfo = array(); $functioninfo['name'] = $function->name; if ($function->component == 'moodle' || $function->component == 'core') { $version = $CFG->version; // Moodle version. } else { $versionpath = core_component::get_component_directory($function->component).'/version.php'; if (is_readable($versionpath)) { // We store the component version once retrieved (so we don't load twice the version.php). if (!isset($componentversions[$function->component])) { $plugin = new stdClass(); include($versionpath); $componentversions[$function->component] = $plugin->version; $version = $plugin->version; } else { $version = $componentversions[$function->component]; } } else { // Ignore this component or plugin, it was probably incorrectly uninstalled. continue; } } $functioninfo['version'] = $version; $availablefunctions[] = $functioninfo; } $siteinfo['functions'] = $availablefunctions; // Mobile CSS theme and alternative login url. $siteinfo['mobilecssurl'] = !empty($CFG->mobilecssurl) ? $CFG->mobilecssurl : ''; // Retrieve some advanced features. Only enable/disable ones (bool). $advancedfeatures = ["usecomments", "usetags", "enablenotes", "messaging", "enableblogs", "enablecompletion", "enablebadges", "messagingallusers", "enablecustomreports", "enableglobalsearch"]; foreach ($advancedfeatures as $feature) { if (isset($CFG->{$feature})) { $siteinfo['advancedfeatures'][] = array( 'name' => $feature, 'value' => (int) $CFG->{$feature} ); } } // Special case mnet_dispatcher_mode. $siteinfo['advancedfeatures'][] = array( 'name' => 'mnet_dispatcher_mode', 'value' => ($CFG->mnet_dispatcher_mode == 'strict') ? 1 : 0 ); // Competencies. $enablecompetencies = get_config('core_competency', 'enabled'); $siteinfo['advancedfeatures'][] = [ 'name' => 'enablecompetencies', 'value' => (!empty($enablecompetencies)) ? 1 : 0, ]; // User can manage own files. $siteinfo['usercanmanageownfiles'] = has_capability('moodle/user:manageownfiles', $context); // User quota. 0 means user can ignore the quota. $siteinfo['userquota'] = 0; if (!has_capability('moodle/user:ignoreuserquota', $context)) { $siteinfo['userquota'] = (int) $CFG->userquota; // Cast to int to ensure value is not higher than PHP_INT_MAX. } // User max upload file size. -1 means the user can ignore the upload file size. // Cast to int to ensure value is not higher than PHP_INT_MAX. $siteinfo['usermaxuploadfilesize'] = (int) get_user_max_upload_file_size($context, $CFG->maxbytes); // User home page. $siteinfo['userhomepage'] = get_home_page(); if ($siteinfo['userhomepage'] === HOMEPAGE_URL) { $siteinfo['userhomepageurl'] = (string) get_default_home_page_url(); } // Calendar. $siteinfo['sitecalendartype'] = $CFG->calendartype; if (empty($USER->calendartype)) { $siteinfo['usercalendartype'] = $CFG->calendartype; } else { $siteinfo['usercalendartype'] = $USER->calendartype; } $siteinfo['userissiteadmin'] = is_siteadmin(); // User key, to avoid using the WS token for fetching assets. $siteinfo['userprivateaccesskey'] = get_user_key('core_files', $USER->id); // Current theme. $siteinfo['theme'] = clean_param($PAGE->theme->name, PARAM_THEME); // We always clean to avoid problem with old sites. $siteinfo['limitconcurrentlogins'] = (int) $CFG->limitconcurrentlogins; if (!empty($CFG->limitconcurrentlogins)) { // For performance, only when enabled. $siteinfo['usersessionscount'] = count(\core\session\manager::get_sessions_by_userid($USER->id)); } $siteinfo['policyagreed'] = $USER->policyagreed; return $siteinfo; } /** * Returns description of method result value * * @return external_single_structure * @since Moodle 2.2 */ public static function get_site_info_returns() { return new external_single_structure( array( 'sitename' => new external_value(PARAM_RAW, 'site name'), 'username' => new external_value(PARAM_RAW, 'username'), 'firstname' => new external_value(PARAM_TEXT, 'first name'), 'lastname' => new external_value(PARAM_TEXT, 'last name'), 'fullname' => new external_value(PARAM_TEXT, 'user full name'), 'lang' => new external_value(PARAM_LANG, 'Current language.'), 'userid' => new external_value(PARAM_INT, 'user id'), 'siteurl' => new external_value(PARAM_RAW, 'site url'), 'userpictureurl' => new external_value(PARAM_URL, 'the user profile picture. Warning: this url is the public URL that only works when forcelogin is set to NO and guestaccess is set to YES. In order to retrieve user profile pictures independently of the Moodle config, replace "pluginfile.php" by "webservice/pluginfile.php?token=WSTOKEN&file=" Of course the user can only see profile picture depending on his/her permissions. Moreover it is recommended to use HTTPS too.'), 'functions' => new external_multiple_structure( new external_single_structure( array( 'name' => new external_value(PARAM_RAW, 'function name'), 'version' => new external_value(PARAM_TEXT, 'The version number of the component to which the function belongs') ), 'functions that are available') ), 'downloadfiles' => new external_value(PARAM_INT, '1 if users are allowed to download files, 0 if not', VALUE_OPTIONAL), 'uploadfiles' => new external_value(PARAM_INT, '1 if users are allowed to upload files, 0 if not', VALUE_OPTIONAL), 'release' => new external_value(PARAM_TEXT, 'Moodle release number', VALUE_OPTIONAL), 'version' => new external_value(PARAM_TEXT, 'Moodle version number', VALUE_OPTIONAL), 'mobilecssurl' => new external_value(PARAM_URL, 'Mobile custom CSS theme', VALUE_OPTIONAL), 'advancedfeatures' => new external_multiple_structure( new external_single_structure( array( 'name' => new external_value(PARAM_ALPHANUMEXT, 'feature name'), 'value' => new external_value(PARAM_INT, 'feature value. Usually 1 means enabled.') ), 'Advanced features availability' ), 'Advanced features availability', VALUE_OPTIONAL ), 'usercanmanageownfiles' => new external_value(PARAM_BOOL, 'true if the user can manage his own files', VALUE_OPTIONAL), 'userquota' => new external_value(PARAM_INT, 'user quota (bytes). 0 means user can ignore the quota', VALUE_OPTIONAL), 'usermaxuploadfilesize' => new external_value(PARAM_INT, 'user max upload file size (bytes). -1 means the user can ignore the upload file size', VALUE_OPTIONAL), 'userhomepage' => new external_value(PARAM_INT, 'the default home page for the user: 0 for the site home, 1 for dashboard', VALUE_OPTIONAL), 'userhomepageurl' => new external_value(PARAM_LOCALURL, 'The URL of default home page when userhomepage is 4 (HOMEPAGE_URL).', VALUE_OPTIONAL), 'userprivateaccesskey' => new external_value(PARAM_ALPHANUM, 'Private user access key for fetching files.', VALUE_OPTIONAL), 'siteid' => new external_value(PARAM_INT, 'Site course ID', VALUE_OPTIONAL), 'sitecalendartype' => new external_value(PARAM_PLUGIN, 'Calendar type set in the site.', VALUE_OPTIONAL), 'usercalendartype' => new external_value(PARAM_PLUGIN, 'Calendar typed used by the user.', VALUE_OPTIONAL), 'userissiteadmin' => new external_value(PARAM_BOOL, 'Whether the user is a site admin or not.', VALUE_OPTIONAL), 'theme' => new external_value(PARAM_THEME, 'Current theme for the user.', VALUE_OPTIONAL), 'limitconcurrentlogins' => new external_value(PARAM_INT, 'Number of concurrent sessions allowed', VALUE_OPTIONAL), 'usersessionscount' => new external_value(PARAM_INT, 'Number of active sessions for current user. Only returned when limitconcurrentlogins is used.', VALUE_OPTIONAL), 'policyagreed' => new external_value(PARAM_INT, 'Whether user accepted all the policies.', VALUE_OPTIONAL), ) ); } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/enrol/externallib.php000064400000201316152266556340023043 0ustar00. /** * Course participations External functions. * * @package core_enrol * @category external * @copyright 2010 Jerome Mouneyrac * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ use core_external\external_api; use core_external\external_files; use core_external\external_format_value; use core_external\external_function_parameters; use core_external\external_multiple_structure; use core_external\external_single_structure; use core_external\external_value; /** * Enrol external functions * * This api is mostly read only, the actual enrol and unenrol * support is in each enrol plugin. * * @package core_enrol * @category external * @copyright 2010 Jerome Mouneyrac * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.2 */ class core_enrol_external extends external_api { /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.4 */ public static function get_enrolled_users_with_capability_parameters() { return new external_function_parameters( array ( 'coursecapabilities' => new external_multiple_structure( new external_single_structure( array ( 'courseid' => new external_value(PARAM_INT, 'Course ID number in the Moodle course table'), 'capabilities' => new external_multiple_structure( new external_value(PARAM_CAPABILITY, 'Capability name, such as mod/forum:viewdiscussion')), ) ) , 'course id and associated capability name'), 'options' => new external_multiple_structure( new external_single_structure( array( 'name' => new external_value(PARAM_ALPHANUMEXT, 'option name'), 'value' => new external_value(PARAM_RAW, 'option value') ) ), 'Option names: * groupid (integer) return only users in this group id. Requires \'moodle/site:accessallgroups\' . * onlyactive (integer) only users with active enrolments. Requires \'moodle/course:enrolreview\' . * userfields (\'string, string, ...\') return only the values of these user fields. * limitfrom (integer) sql limit from. * limitnumber (integer) max number of users per course and capability.', VALUE_DEFAULT, array()) ) ); } /** * Return users that have the capabilities for each course specified. For each course and capability specified, * a list of the users that are enrolled in the course and have that capability are returned. * * @param array $coursecapabilities array of course ids and associated capability names {courseid, {capabilities}} * @return array An array of arrays describing users for each associated courseid and capability * @since Moodle 2.4 */ public static function get_enrolled_users_with_capability($coursecapabilities, $options) { global $CFG, $DB; require_once($CFG->dirroot . '/course/lib.php'); require_once($CFG->dirroot . "/user/lib.php"); if (empty($coursecapabilities)) { throw new invalid_parameter_exception('Parameter can not be empty'); } $params = self::validate_parameters(self::get_enrolled_users_with_capability_parameters(), array ('coursecapabilities' => $coursecapabilities, 'options'=>$options)); $result = array(); $userlist = array(); $groupid = 0; $onlyactive = false; $userfields = array(); $limitfrom = 0; $limitnumber = 0; foreach ($params['options'] as $option) { switch ($option['name']) { case 'groupid': $groupid = (int)$option['value']; break; case 'onlyactive': $onlyactive = !empty($option['value']); break; case 'userfields': $thefields = explode(',', $option['value']); foreach ($thefields as $f) { $userfields[] = clean_param($f, PARAM_ALPHANUMEXT); } break; case 'limitfrom' : $limitfrom = clean_param($option['value'], PARAM_INT); break; case 'limitnumber' : $limitnumber = clean_param($option['value'], PARAM_INT); break; } } foreach ($params['coursecapabilities'] as $coursecapability) { $courseid = $coursecapability['courseid']; $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST); $coursecontext = context_course::instance($courseid); if (!$coursecontext) { throw new moodle_exception('cannotfindcourse', 'error', '', null, 'The course id ' . $courseid . ' doesn\'t exist.'); } if ($courseid == SITEID) { $context = context_system::instance(); } else { $context = $coursecontext; } try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $params['courseid']; throw new moodle_exception(get_string('errorcoursecontextnotvalid' , 'webservice', $exceptionparam)); } course_require_view_participants($context); // The accessallgroups capability is needed to use this option. if (!empty($groupid) && groups_is_member($groupid)) { require_capability('moodle/site:accessallgroups', $coursecontext); } // The course:enrolereview capability is needed to use this option. if ($onlyactive) { require_capability('moodle/course:enrolreview', $coursecontext); } // To see the permissions of others role:review capability is required. require_capability('moodle/role:review', $coursecontext); foreach ($coursecapability['capabilities'] as $capability) { $courseusers['courseid'] = $courseid; $courseusers['capability'] = $capability; list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $capability, $groupid, $onlyactive); $enrolledparams['courseid'] = $courseid; $sql = "SELECT u.*, COALESCE(ul.timeaccess, 0) AS lastcourseaccess FROM {user} u LEFT JOIN {user_lastaccess} ul ON (ul.userid = u.id AND ul.courseid = :courseid) WHERE u.id IN ($enrolledsql) ORDER BY u.id ASC"; $enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams, $limitfrom, $limitnumber); $users = array(); foreach ($enrolledusers as $courseuser) { if ($userdetails = user_get_user_details($courseuser, $course, $userfields)) { $users[] = $userdetails; } } $enrolledusers->close(); $courseusers['users'] = $users; $result[] = $courseusers; } } return $result; } /** * Returns description of method result value * * @return external_multiple_structure * @since Moodle 2.4 */ public static function get_enrolled_users_with_capability_returns() { return new external_multiple_structure( new external_single_structure ( array ( 'courseid' => new external_value(PARAM_INT, 'Course ID number in the Moodle course table'), 'capability' => new external_value(PARAM_CAPABILITY, 'Capability name'), 'users' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'ID of the user'), 'username' => new external_value(PARAM_RAW, 'Username', VALUE_OPTIONAL), 'firstname' => new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL), 'lastname' => new external_value(PARAM_NOTAGS, 'The family name of the user', VALUE_OPTIONAL), 'fullname' => new external_value(PARAM_NOTAGS, 'The fullname of the user'), 'email' => new external_value(PARAM_TEXT, 'Email address', VALUE_OPTIONAL), 'address' => new external_value(PARAM_TEXT, 'Postal address', VALUE_OPTIONAL), 'phone1' => new external_value(PARAM_NOTAGS, 'Phone 1', VALUE_OPTIONAL), 'phone2' => new external_value(PARAM_NOTAGS, 'Phone 2', VALUE_OPTIONAL), 'department' => new external_value(PARAM_TEXT, 'department', VALUE_OPTIONAL), 'institution' => new external_value(PARAM_TEXT, 'institution', VALUE_OPTIONAL), 'interests' => new external_value(PARAM_TEXT, 'user interests (separated by commas)', VALUE_OPTIONAL), 'firstaccess' => new external_value(PARAM_INT, 'first access to the site (0 if never)', VALUE_OPTIONAL), 'lastaccess' => new external_value(PARAM_INT, 'last access to the site (0 if never)', VALUE_OPTIONAL), 'lastcourseaccess' => new external_value(PARAM_INT, 'last access to the course (0 if never)', VALUE_OPTIONAL), 'description' => new external_value(PARAM_RAW, 'User profile description', VALUE_OPTIONAL), 'descriptionformat' => new external_value(PARAM_INT, 'User profile description format', VALUE_OPTIONAL), 'city' => new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL), 'country' => new external_value(PARAM_ALPHA, 'Country code of the user, such as AU or CZ', VALUE_OPTIONAL), 'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small', VALUE_OPTIONAL), 'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big', VALUE_OPTIONAL), 'customfields' => new external_multiple_structure( new external_single_structure( array( 'type' => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field'), 'value' => new external_value(PARAM_RAW, 'The value of the custom field'), 'name' => new external_value(PARAM_RAW, 'The name of the custom field'), 'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field'), ) ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL), 'groups' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'group id'), 'name' => new external_value(PARAM_RAW, 'group name'), 'description' => new external_value(PARAM_RAW, 'group description'), ) ), 'user groups', VALUE_OPTIONAL), 'roles' => new external_multiple_structure( new external_single_structure( array( 'roleid' => new external_value(PARAM_INT, 'role id'), 'name' => new external_value(PARAM_RAW, 'role name'), 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'role shortname'), 'sortorder' => new external_value(PARAM_INT, 'role sortorder') ) ), 'user roles', VALUE_OPTIONAL), 'preferences' => new external_multiple_structure( new external_single_structure( array( 'name' => new external_value(PARAM_RAW, 'The name of the preferences'), 'value' => new external_value(PARAM_RAW, 'The value of the custom field'), ) ), 'User preferences', VALUE_OPTIONAL), 'enrolledcourses' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'Id of the course'), 'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'), 'shortname' => new external_value(PARAM_RAW, 'Shortname of the course') ) ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL) ) ), 'List of users that are enrolled in the course and have the specified capability'), ) ) ); } /** * Returns description of method parameters * * @return external_function_parameters */ public static function get_users_courses_parameters() { return new external_function_parameters( array( 'userid' => new external_value(PARAM_INT, 'user id'), 'returnusercount' => new external_value(PARAM_BOOL, 'Include count of enrolled users for each course? This can add several seconds to the response time' . ' if a user is on several large courses, so set this to false if the value will not be used to' . ' improve performance.', VALUE_DEFAULT, true), ) ); } /** * Get list of courses user is enrolled in (only active enrolments are returned). * Please note the current user must be able to access the course, otherwise the course is not included. * * @param int $userid * @param bool $returnusercount * @return array of courses */ public static function get_users_courses($userid, $returnusercount = true) { 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 $params = self::validate_parameters(self::get_users_courses_parameters(), ['userid' => $userid, 'returnusercount' => $returnusercount]); $userid = $params['userid']; $returnusercount = $params['returnusercount']; $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)); } } foreach ($courses as $course) { $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); } } $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); } $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' => $course->startdate, 'enddate' => $course->enddate, 'marker' => $course->marker, 'lastaccess' => $lastaccess, 'isfavourite' => isset($favouritecourseids[$course->id]), 'hidden' => $hidden, 'overviewfiles' => $overviewfiles, 'showactivitydates' => $course->showactivitydates, 'showcompletionconditions' => $course->showcompletionconditions, 'timemodified' => $course->timemodified, ]; if ($returnusercount) { $courseresult['enrolledusercount'] = $enrolledusercount; } $result[] = $courseresult; } return $result; } /** * Returns description of method result value * * @return \core_external\external_description */ public static function get_users_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), ) ) ); } /** * Returns description of method parameters value * * @return \core_external\external_description */ public static function get_potential_users_parameters() { return new external_function_parameters( array( 'courseid' => new external_value(PARAM_INT, 'course id'), 'enrolid' => new external_value(PARAM_INT, 'enrolment id'), 'search' => new external_value(PARAM_RAW, 'query'), 'searchanywhere' => new external_value(PARAM_BOOL, 'find a match anywhere, or only at the beginning'), 'page' => new external_value(PARAM_INT, 'Page number'), 'perpage' => new external_value(PARAM_INT, 'Number per page'), ) ); } /** * Get potential users. * * @param int $courseid Course id * @param int $enrolid Enrolment id * @param string $search The query * @param boolean $searchanywhere Match anywhere in the string * @param int $page Page number * @param int $perpage Max per page * @return array An array of users */ public static function get_potential_users($courseid, $enrolid, $search, $searchanywhere, $page, $perpage) { global $PAGE, $DB, $CFG; require_once($CFG->dirroot.'/enrol/locallib.php'); require_once($CFG->dirroot.'/user/lib.php'); $params = self::validate_parameters( self::get_potential_users_parameters(), array( 'courseid' => $courseid, 'enrolid' => $enrolid, 'search' => $search, 'searchanywhere' => $searchanywhere, 'page' => $page, 'perpage' => $perpage ) ); $context = context_course::instance($params['courseid']); try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $params['courseid']; throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam); } require_capability('moodle/course:enrolreview', $context); $course = $DB->get_record('course', array('id' => $params['courseid'])); $manager = new course_enrolment_manager($PAGE, $course); $users = $manager->get_potential_users($params['enrolid'], $params['search'], $params['searchanywhere'], $params['page'], $params['perpage']); $results = array(); // Add also extra user fields. $identityfields = \core_user\fields::get_identity_fields($context, true); $customprofilefields = []; foreach ($identityfields as $key => $value) { if ($fieldname = \core_user\fields::match_custom_field($value)) { unset($identityfields[$key]); $customprofilefields[$fieldname] = true; } } if ($customprofilefields) { $identityfields[] = 'customfields'; } $requiredfields = array_merge( ['id', 'fullname', 'profileimageurl', 'profileimageurlsmall'], $identityfields ); foreach ($users['users'] as $id => $user) { // Note: We pass the course here to validate that the current user can at least view user details in this course. // The user we are looking at is not in this course yet though - but we only fetch the minimal set of // user records, and the user has been validated to have course:enrolreview in this course. Otherwise // there is no way to find users who aren't in the course in order to enrol them. if ($userdetails = user_get_user_details($user, $course, $requiredfields)) { // For custom fields, only return the ones we actually need. if ($customprofilefields && array_key_exists('customfields', $userdetails)) { foreach ($userdetails['customfields'] as $key => $data) { if (!array_key_exists($data['shortname'], $customprofilefields)) { unset($userdetails['customfields'][$key]); } } $userdetails['customfields'] = array_values($userdetails['customfields']); } $results[] = $userdetails; } } return $results; } /** * Returns description of method result value * * @return \core_external\external_description */ public static function get_potential_users_returns() { global $CFG; require_once($CFG->dirroot . '/user/externallib.php'); return new external_multiple_structure(core_user_external::user_description()); } /** * Returns description of method parameters * * @return external_function_parameters */ public static function search_users_parameters(): external_function_parameters { return new external_function_parameters( [ 'courseid' => new external_value(PARAM_INT, 'course id'), 'search' => new external_value(PARAM_RAW, 'query'), 'searchanywhere' => new external_value(PARAM_BOOL, 'find a match anywhere, or only at the beginning'), 'page' => new external_value(PARAM_INT, 'Page number'), 'perpage' => new external_value(PARAM_INT, 'Number per page'), 'contextid' => new external_value(PARAM_INT, 'Context ID', VALUE_DEFAULT, null), ] ); } /** * Search course participants. * * @param int $courseid Course id * @param string $search The query * @param bool $searchanywhere Match anywhere in the string * @param int $page Page number * @param int $perpage Max per page * @param ?int $contextid Context ID we are in - we might use search on activity level and its group mode can be different from course group mode. * @return array An array of users * @throws moodle_exception */ public static function search_users(int $courseid, string $search, bool $searchanywhere, int $page, int $perpage, ?int $contextid = null): array { global $PAGE, $CFG; require_once($CFG->dirroot.'/enrol/locallib.php'); require_once($CFG->dirroot.'/user/lib.php'); $params = self::validate_parameters( self::search_users_parameters(), [ 'courseid' => $courseid, 'search' => $search, 'searchanywhere' => $searchanywhere, 'page' => $page, 'perpage' => $perpage, 'contextid' => $contextid, ], ); if (isset($contextid)) { $context = context::instance_by_id($params['contextid']); } else { $context = context_course::instance($params['courseid']); } try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $params['courseid']; throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam); } course_require_view_participants($context); $course = get_course($params['courseid']); $manager = new course_enrolment_manager($PAGE, $course); $users = $manager->search_users( $params['search'], $params['searchanywhere'], $params['page'], $params['perpage'], false, $params['contextid'] ); $results = []; // Add also extra user fields. $requiredfields = array_merge( ['id', 'fullname', 'profileimageurl', 'profileimageurlsmall'], // TODO Does not support custom user profile fields (MDL-70456). \core_user\fields::get_identity_fields($context, false) ); foreach ($users['users'] as $user) { if ($userdetails = user_get_user_details($user, $course, $requiredfields)) { $results[] = $userdetails; } } return $results; } /** * Returns description of method result value * * @return external_multiple_structure */ public static function search_users_returns(): external_multiple_structure { global $CFG; require_once($CFG->dirroot . '/user/externallib.php'); return new external_multiple_structure(core_user_external::user_description()); } /** * Returns description of method parameters * * @return external_function_parameters */ public static function get_enrolled_users_parameters() { return new external_function_parameters( [ 'courseid' => new external_value(PARAM_INT, 'course id'), 'options' => new external_multiple_structure( new external_single_structure( [ 'name' => new external_value(PARAM_ALPHANUMEXT, 'option name'), 'value' => new external_value(PARAM_RAW, 'option value') ] ), 'Option names: * withcapability (string) return only users with this capability. This option requires \'moodle/role:review\' on the course context. * groupid (integer) return only users in this group id. If the course has groups enabled and this param isn\'t defined, returns all the viewable users. This option requires \'moodle/site:accessallgroups\' on the course context if the user doesn\'t belong to the group. * onlyactive (integer) return only users with active enrolments and matching time restrictions. This option requires \'moodle/course:enrolreview\' on the course context. Please note that this option can\'t be used together with onlysuspended (only one can be active). * onlysuspended (integer) return only suspended users. This option requires \'moodle/course:enrolreview\' on the course context. Please note that this option can\'t be used together with onlyactive (only one can be active). * userfields (\'string, string, ...\') return only the values of these user fields. * limitfrom (integer) sql limit from. * limitnumber (integer) maximum number of returned users. * sortby (string) sort by id, firstname or lastname. For ordering like the site does, use siteorder. * sortdirection (string) ASC or DESC', VALUE_DEFAULT, []), ] ); } /** * Get course participants details * * @param int $courseid course id * @param array $options options { * 'name' => option name * 'value' => option value * } * @return array An array of users */ public static function get_enrolled_users($courseid, $options = []) { global $CFG, $USER, $DB; require_once($CFG->dirroot . '/course/lib.php'); require_once($CFG->dirroot . "/user/lib.php"); $params = self::validate_parameters( self::get_enrolled_users_parameters(), [ 'courseid'=>$courseid, 'options'=>$options ] ); $withcapability = ''; $groupid = 0; $onlyactive = false; $onlysuspended = false; $userfields = []; $limitfrom = 0; $limitnumber = 0; $sortby = 'us.id'; $sortparams = []; $sortdirection = 'ASC'; foreach ($options as $option) { switch ($option['name']) { case 'withcapability': $withcapability = $option['value']; break; case 'groupid': $groupid = (int)$option['value']; break; case 'onlyactive': $onlyactive = !empty($option['value']); break; case 'onlysuspended': $onlysuspended = !empty($option['value']); break; case 'userfields': $thefields = explode(',', $option['value']); foreach ($thefields as $f) { $userfields[] = clean_param($f, PARAM_ALPHANUMEXT); } break; case 'limitfrom' : $limitfrom = clean_param($option['value'], PARAM_INT); break; case 'limitnumber' : $limitnumber = clean_param($option['value'], PARAM_INT); break; case 'sortby': $sortallowedvalues = ['id', 'firstname', 'lastname', 'siteorder']; if (!in_array($option['value'], $sortallowedvalues)) { throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $option['value'] . '), allowed values are: ' . implode(',', $sortallowedvalues)); } if ($option['value'] == 'siteorder') { list($sortby, $sortparams) = users_order_by_sql('us'); } else { $sortby = 'us.' . $option['value']; } break; case 'sortdirection': $sortdirection = strtoupper($option['value']); $directionallowedvalues = ['ASC', 'DESC']; if (!in_array($sortdirection, $directionallowedvalues)) { throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' . 'allowed values are: ' . implode(',', $directionallowedvalues)); } break; } } $course = $DB->get_record('course', ['id' => $courseid], '*', MUST_EXIST); $coursecontext = context_course::instance($courseid, IGNORE_MISSING); if ($courseid == SITEID) { $context = context_system::instance(); } else { $context = $coursecontext; } try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $params['courseid']; throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam); } course_require_view_participants($context); // to overwrite this parameter, you need role:review capability if ($withcapability) { require_capability('moodle/role:review', $coursecontext); } // need accessallgroups capability if you want to overwrite this option if (!empty($groupid) && !groups_is_member($groupid)) { require_capability('moodle/site:accessallgroups', $coursecontext); } // to overwrite this option, you need course:enrolereview permission if ($onlyactive || $onlysuspended) { require_capability('moodle/course:enrolreview', $coursecontext); } list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive, $onlysuspended); $ctxselect = ', ' . context_helper::get_preload_record_columns_sql('ctx'); $ctxjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel)"; $enrolledparams['contextlevel'] = CONTEXT_USER; $groupjoin = ''; if (empty($groupid) && groups_get_course_groupmode($course) == SEPARATEGROUPS && !has_capability('moodle/site:accessallgroups', $coursecontext)) { // Filter by groups the user can view. $usergroups = groups_get_user_groups($course->id); if (!empty($usergroups['0'])) { list($groupsql, $groupparams) = $DB->get_in_or_equal($usergroups['0'], SQL_PARAMS_NAMED); $groupjoin = "JOIN {groups_members} gm ON (u.id = gm.userid AND gm.groupid $groupsql)"; $enrolledparams = array_merge($enrolledparams, $groupparams); } else { // User doesn't belong to any group, so he can't see any user. Return an empty array. return []; } } $sql = "SELECT us.*, COALESCE(ul.timeaccess, 0) AS lastcourseaccess FROM {user} us JOIN ( SELECT DISTINCT u.id $ctxselect FROM {user} u $ctxjoin $groupjoin WHERE u.id IN ($enrolledsql) ) q ON q.id = us.id LEFT JOIN {user_lastaccess} ul ON (ul.userid = us.id AND ul.courseid = :courseid) ORDER BY $sortby $sortdirection"; $enrolledparams = array_merge($enrolledparams, $sortparams); $enrolledparams['courseid'] = $courseid; $enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams, $limitfrom, $limitnumber); $users = []; foreach ($enrolledusers as $user) { context_helper::preload_from_record($user); if ($userdetails = user_get_user_details($user, $course, $userfields)) { $users[] = $userdetails; } } $enrolledusers->close(); return $users; } /** * Returns description of method result value * * @return \core_external\external_description */ public static function get_enrolled_users_returns() { return new external_multiple_structure( new external_single_structure( [ 'id' => new external_value(PARAM_INT, 'ID of the user'), 'username' => new external_value(PARAM_RAW, 'Username policy is defined in Moodle security config', VALUE_OPTIONAL), 'firstname' => new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL), 'lastname' => new external_value(PARAM_NOTAGS, 'The family name of the user', VALUE_OPTIONAL), 'fullname' => new external_value(PARAM_NOTAGS, 'The fullname of the user'), 'email' => new external_value(PARAM_TEXT, 'An email address - allow email as root@localhost', VALUE_OPTIONAL), 'address' => new external_value(PARAM_TEXT, 'Postal address', VALUE_OPTIONAL), 'phone1' => new external_value(PARAM_NOTAGS, 'Phone 1', VALUE_OPTIONAL), 'phone2' => new external_value(PARAM_NOTAGS, 'Phone 2', VALUE_OPTIONAL), 'department' => new external_value(PARAM_TEXT, 'department', VALUE_OPTIONAL), 'institution' => new external_value(PARAM_TEXT, 'institution', VALUE_OPTIONAL), 'idnumber' => new external_value(PARAM_RAW, 'An arbitrary ID code number perhaps from the institution', VALUE_OPTIONAL), 'interests' => new external_value(PARAM_TEXT, 'user interests (separated by commas)', VALUE_OPTIONAL), 'firstaccess' => new external_value(PARAM_INT, 'first access to the site (0 if never)', VALUE_OPTIONAL), 'lastaccess' => new external_value(PARAM_INT, 'last access to the site (0 if never)', VALUE_OPTIONAL), 'lastcourseaccess' => new external_value(PARAM_INT, 'last access to the course (0 if never)', VALUE_OPTIONAL), 'description' => new external_value(PARAM_RAW, 'User profile description', VALUE_OPTIONAL), 'descriptionformat' => new external_format_value('description', VALUE_OPTIONAL), 'city' => new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL), 'country' => new external_value(PARAM_ALPHA, 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL), 'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small version', VALUE_OPTIONAL), 'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version', VALUE_OPTIONAL), 'customfields' => new external_multiple_structure( new external_single_structure( [ 'type' => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field - text field, checkbox...'), 'value' => new external_value(PARAM_RAW, 'The value of the custom field'), 'name' => new external_value(PARAM_RAW, 'The name of the custom field'), 'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field - to be able to build the field class in the code'), ] ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL), 'groups' => new external_multiple_structure( new external_single_structure( [ 'id' => new external_value(PARAM_INT, 'group id'), 'name' => new external_value(PARAM_RAW, 'group name'), 'description' => new external_value(PARAM_RAW, 'group description'), 'descriptionformat' => new external_format_value('description'), ] ), 'user groups', VALUE_OPTIONAL), 'roles' => new external_multiple_structure( new external_single_structure( [ 'roleid' => new external_value(PARAM_INT, 'role id'), 'name' => new external_value(PARAM_RAW, 'role name'), 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'role shortname'), 'sortorder' => new external_value(PARAM_INT, 'role sortorder') ] ), 'user roles', VALUE_OPTIONAL), 'preferences' => new external_multiple_structure( new external_single_structure( [ 'name' => new external_value(PARAM_RAW, 'The name of the preferences'), 'value' => new external_value(PARAM_RAW, 'The value of the custom field'), ] ), 'User preferences', VALUE_OPTIONAL), 'enrolledcourses' => new external_multiple_structure( new external_single_structure( [ 'id' => new external_value(PARAM_INT, 'Id of the course'), 'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'), 'shortname' => new external_value(PARAM_RAW, 'Shortname of the course') ] ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL) ] ) ); } /** * Returns description of get_course_enrolment_methods() parameters * * @return external_function_parameters */ public static function get_course_enrolment_methods_parameters() { return new external_function_parameters( array( 'courseid' => new external_value(PARAM_INT, 'Course id') ) ); } /** * Get list of active course enrolment methods for current user. * * @param int $courseid * @return array of course enrolment methods * @throws moodle_exception */ public static function get_course_enrolment_methods($courseid) { global $DB; $params = self::validate_parameters(self::get_course_enrolment_methods_parameters(), array('courseid' => $courseid)); self::validate_context(context_system::instance()); $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST); if (!core_course_category::can_view_course_info($course) && !can_access_course($course)) { throw new moodle_exception('coursehidden'); } $result = array(); $enrolinstances = enrol_get_instances($params['courseid'], true); foreach ($enrolinstances as $enrolinstance) { if ($enrolplugin = enrol_get_plugin($enrolinstance->enrol)) { if ($instanceinfo = $enrolplugin->get_enrol_info($enrolinstance)) { $result[] = (array) $instanceinfo; } } } return $result; } /** * Returns description of get_course_enrolment_methods() result value * * @return \core_external\external_description */ public static function get_course_enrolment_methods_returns() { return new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'id of course enrolment instance'), 'courseid' => new external_value(PARAM_INT, 'id of course'), 'type' => new external_value(PARAM_PLUGIN, 'type of enrolment plugin'), 'name' => new external_value(PARAM_RAW, 'name of enrolment plugin'), 'status' => new external_value(PARAM_RAW, 'status of enrolment plugin'), 'wsfunction' => new external_value(PARAM_ALPHANUMEXT, 'webservice function to get more information', VALUE_OPTIONAL), ) ) ); } /** * Returns description of submit_user_enrolment_form parameters. * * @return external_function_parameters */ public static function submit_user_enrolment_form_parameters() { return new external_function_parameters([ 'formdata' => new external_value(PARAM_RAW, 'The data from the event form'), ]); } /** * External function that handles the user enrolment form submission. * * @param string $formdata The user enrolment form data in s URI encoded param string * @return array An array consisting of the processing result and error flag, if available */ public static function submit_user_enrolment_form($formdata) { global $CFG, $DB, $PAGE; // Parameter validation. $params = self::validate_parameters(self::submit_user_enrolment_form_parameters(), ['formdata' => $formdata]); $data = []; parse_str($params['formdata'], $data); $userenrolment = $DB->get_record('user_enrolments', ['id' => $data['ue']], '*', MUST_EXIST); $instance = $DB->get_record('enrol', ['id' => $userenrolment->enrolid], '*', MUST_EXIST); $plugin = enrol_get_plugin($instance->enrol); $course = get_course($instance->courseid); $context = context_course::instance($course->id); self::validate_context($context); require_once("$CFG->dirroot/enrol/editenrolment_form.php"); $customformdata = [ 'ue' => $userenrolment, 'modal' => true, 'enrolinstancename' => $plugin->get_instance_name($instance) ]; $mform = new enrol_user_enrolment_form(null, $customformdata, 'post', '', null, true, $data); if ($validateddata = $mform->get_data()) { if (!empty($validateddata->duration) && $validateddata->timeend == 0) { $validateddata->timeend = $validateddata->timestart + $validateddata->duration; } require_once($CFG->dirroot . '/enrol/locallib.php'); $manager = new course_enrolment_manager($PAGE, $course); $result = $manager->edit_enrolment($userenrolment, $validateddata); return ['result' => $result]; } else { return ['result' => false, 'validationerror' => true]; } } /** * Returns description of submit_user_enrolment_form() result value * * @return \core_external\external_description */ public static function submit_user_enrolment_form_returns() { return new external_single_structure([ 'result' => new external_value(PARAM_BOOL, 'True if the user\'s enrolment was successfully updated'), 'validationerror' => new external_value(PARAM_BOOL, 'Indicates invalid form data', VALUE_DEFAULT, false), ]); } /** * Returns description of unenrol_user_enrolment() parameters * * @return external_function_parameters */ public static function unenrol_user_enrolment_parameters() { return new external_function_parameters( array( 'ueid' => new external_value(PARAM_INT, 'User enrolment ID') ) ); } /** * External function that unenrols a given user enrolment. * * @param int $ueid The user enrolment ID. * @return array An array consisting of the processing result, errors. */ public static function unenrol_user_enrolment($ueid) { global $CFG, $DB, $PAGE; $params = self::validate_parameters(self::unenrol_user_enrolment_parameters(), [ 'ueid' => $ueid ]); $result = false; $errors = []; $userenrolment = $DB->get_record('user_enrolments', ['id' => $params['ueid']], '*'); if ($userenrolment) { $userid = $userenrolment->userid; $enrolid = $userenrolment->enrolid; $enrol = $DB->get_record('enrol', ['id' => $enrolid], '*', MUST_EXIST); $courseid = $enrol->courseid; $course = get_course($courseid); $context = context_course::instance($course->id); self::validate_context($context); } else { $validationerrors['invalidrequest'] = get_string('invalidrequest', 'enrol'); } // If the userenrolment exists, unenrol the user. if (!isset($validationerrors)) { require_once($CFG->dirroot . '/enrol/locallib.php'); $manager = new course_enrolment_manager($PAGE, $course); $result = $manager->unenrol_user($userenrolment); } else { foreach ($validationerrors as $key => $errormessage) { $errors[] = (object)[ 'key' => $key, 'message' => $errormessage ]; } } return [ 'result' => $result, 'errors' => $errors, ]; } /** * Returns description of unenrol_user_enrolment() result value * * @return \core_external\external_description */ public static function unenrol_user_enrolment_returns() { return new external_single_structure( array( 'result' => new external_value(PARAM_BOOL, 'True if the user\'s enrolment was successfully updated'), 'errors' => new external_multiple_structure( new external_single_structure( array( 'key' => new external_value(PARAM_TEXT, 'The data that failed the validation'), 'message' => new external_value(PARAM_TEXT, 'The error message'), ) ), 'List of validation errors' ), ) ); } } /** * Role external functions * * @package core_role * @category external * @copyright 2011 Jerome Mouneyrac * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.2 */ class core_role_external extends external_api { /** * Returns description of method parameters * * @return external_function_parameters */ public static function assign_roles_parameters() { return new external_function_parameters( array( 'assignments' => new external_multiple_structure( new external_single_structure( array( 'roleid' => new external_value(PARAM_INT, 'Role to assign to the user'), 'userid' => new external_value(PARAM_INT, 'The user that is going to be assigned'), 'contextid' => new external_value(PARAM_INT, 'The context to assign the user role in', VALUE_OPTIONAL), 'contextlevel' => new external_value(PARAM_ALPHA, 'The context level to assign the user role in (block, course, coursecat, system, user, module)', VALUE_OPTIONAL), 'instanceid' => new external_value(PARAM_INT, 'The Instance id of item where the role needs to be assigned', VALUE_OPTIONAL), ) ) ) ) ); } /** * Manual role assignments to users * * @param array $assignments An array of manual role assignment */ public static function assign_roles($assignments) { global $DB; // Do basic automatic PARAM checks on incoming data, using params description // If any problems are found then exceptions are thrown with helpful error messages $params = self::validate_parameters(self::assign_roles_parameters(), array('assignments'=>$assignments)); $transaction = $DB->start_delegated_transaction(); foreach ($params['assignments'] as $assignment) { // Ensure correct context level with a instance id or contextid is passed. $context = self::get_context_from_params($assignment); // Ensure the current user is allowed to run this function in the enrolment context. self::validate_context($context); require_capability('moodle/role:assign', $context); // throw an exception if user is not able to assign the role in this context $roles = get_assignable_roles($context, ROLENAME_SHORT); if (!array_key_exists($assignment['roleid'], $roles)) { throw new invalid_parameter_exception('Can not assign roleid='.$assignment['roleid'].' in contextid='.$assignment['contextid']); } role_assign($assignment['roleid'], $assignment['userid'], $context->id); } $transaction->allow_commit(); } /** * Returns description of method result value * * @return null */ public static function assign_roles_returns() { return null; } /** * Returns description of method parameters * * @return external_function_parameters */ public static function unassign_roles_parameters() { return new external_function_parameters( array( 'unassignments' => new external_multiple_structure( new external_single_structure( array( 'roleid' => new external_value(PARAM_INT, 'Role to assign to the user'), 'userid' => new external_value(PARAM_INT, 'The user that is going to be assigned'), 'contextid' => new external_value(PARAM_INT, 'The context to unassign the user role from', VALUE_OPTIONAL), 'contextlevel' => new external_value(PARAM_ALPHA, 'The context level to unassign the user role in + (block, course, coursecat, system, user, module)', VALUE_OPTIONAL), 'instanceid' => new external_value(PARAM_INT, 'The Instance id of item where the role needs to be unassigned', VALUE_OPTIONAL), ) ) ) ) ); } /** * Unassign roles from users * * @param array $unassignments An array of unassignment */ public static function unassign_roles($unassignments) { global $DB; // Do basic automatic PARAM checks on incoming data, using params description // If any problems are found then exceptions are thrown with helpful error messages $params = self::validate_parameters(self::unassign_roles_parameters(), array('unassignments'=>$unassignments)); $transaction = $DB->start_delegated_transaction(); foreach ($params['unassignments'] as $unassignment) { // Ensure the current user is allowed to run this function in the unassignment context $context = self::get_context_from_params($unassignment); self::validate_context($context); require_capability('moodle/role:assign', $context); // throw an exception if user is not able to unassign the role in this context $roles = get_assignable_roles($context, ROLENAME_SHORT); if (!array_key_exists($unassignment['roleid'], $roles)) { throw new invalid_parameter_exception('Can not unassign roleid='.$unassignment['roleid'].' in contextid='.$unassignment['contextid']); } role_unassign($unassignment['roleid'], $unassignment['userid'], $context->id); } $transaction->allow_commit(); } /** * Returns description of method result value * * @return null */ public static function unassign_roles_returns() { return null; } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/calendar/externallib.php000064400000173154152266564300023500 0ustar00. /** * External calendar API * * @package core_calendar * @category external * @copyright 2012 Ankit Agarwal * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.5 */ defined('MOODLE_INTERNAL') || die; require_once($CFG->dirroot . '/calendar/lib.php'); use core_calendar\local\api as local_api; use core_calendar\local\event\container as event_container; use core_calendar\local\event\forms\create as create_event_form; use core_calendar\local\event\forms\update as update_event_form; use core_calendar\local\event\mappers\create_update_form_mapper; use core_calendar\external\event_exporter; use core_calendar\external\events_exporter; use core_calendar\external\events_grouped_by_course_exporter; use core_calendar\external\events_related_objects_cache; use core_external\external_api; use core_external\external_format_value; use core_external\external_function_parameters; use core_external\external_multiple_structure; use core_external\external_single_structure; use core_external\external_value; use core_external\external_warnings; use core_external\util as external_util; /** * Calendar external functions * * @package core_calendar * @category external * @copyright 2012 Ankit Agarwal * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.5 */ class core_calendar_external extends external_api { /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.5 */ public static function delete_calendar_events_parameters() { return new external_function_parameters( array('events' => new external_multiple_structure( new external_single_structure( array( 'eventid' => new external_value(PARAM_INT, 'Event ID', VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'repeat' => new external_value(PARAM_BOOL, 'Delete comeplete series if repeated event') ), 'List of events to delete' ) ) ) ); } /** * Delete Calendar events * * @param array $eventids A list of event ids with repeat flag to delete * @return null * @since Moodle 2.5 */ public static function delete_calendar_events($events) { global $DB; // Parameter validation. $params = self::validate_parameters(self:: delete_calendar_events_parameters(), array('events' => $events)); $transaction = $DB->start_delegated_transaction(); foreach ($params['events'] as $event) { $eventobj = calendar_event::load($event['eventid']); // Let's check if the user is allowed to delete an event. if (!calendar_delete_event_allowed($eventobj)) { throw new moodle_exception('nopermissions', 'error', '', get_string('deleteevent', 'calendar')); } // Time to do the magic. $eventobj->delete($event['repeat']); } // Everything done smoothly, let's commit. $transaction->allow_commit(); return null; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.5 */ public static function delete_calendar_events_returns() { return null; } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.5 */ public static function get_calendar_events_parameters() { return new external_function_parameters( array('events' => new external_single_structure( array( 'eventids' => new external_multiple_structure( new external_value(PARAM_INT, 'event ids') , 'List of event ids', VALUE_DEFAULT, array()), 'courseids' => new external_multiple_structure( new external_value(PARAM_INT, 'course ids') , 'List of course ids for which events will be returned', VALUE_DEFAULT, array()), 'groupids' => new external_multiple_structure( new external_value(PARAM_INT, 'group ids') , 'List of group ids for which events should be returned', VALUE_DEFAULT, array()), 'categoryids' => new external_multiple_structure( new external_value(PARAM_INT, 'Category ids'), 'List of category ids for which events will be returned', VALUE_DEFAULT, array()), ), 'Event details', VALUE_DEFAULT, array()), 'options' => new external_single_structure( array( 'userevents' => new external_value(PARAM_BOOL, "Set to true to return current user's user events", VALUE_DEFAULT, true, NULL_ALLOWED), 'siteevents' => new external_value(PARAM_BOOL, "Set to true to return site events", VALUE_DEFAULT, true, NULL_ALLOWED), 'timestart' => new external_value(PARAM_INT, "Time from which events should be returned", VALUE_DEFAULT, 0, NULL_ALLOWED), 'timeend' => new external_value(PARAM_INT, "Time to which the events should be returned. We treat 0 and null as no end", VALUE_DEFAULT, 0, NULL_ALLOWED), 'ignorehidden' => new external_value(PARAM_BOOL, "Ignore hidden events or not", VALUE_DEFAULT, true, NULL_ALLOWED), ), 'Options', VALUE_DEFAULT, array()) ) ); } /** * Get Calendar events * * @param array $events A list of events * @param array $options various options * @return array Array of event details * @since Moodle 2.5 */ public static function get_calendar_events($events = array(), $options = array()) { global $SITE, $DB, $USER; // Parameter validation. $params = self::validate_parameters(self::get_calendar_events_parameters(), array('events' => $events, 'options' => $options)); $funcparam = array('courses' => array(), 'groups' => array(), 'categories' => array()); $hassystemcap = has_capability('moodle/calendar:manageentries', context_system::instance()); $warnings = array(); $coursecategories = array(); // Let us find out courses and their categories that we can return events from. if (!$hassystemcap) { $courseobjs = enrol_get_my_courses(); $courses = array_keys($courseobjs); $coursecategories = array_flip(array_map(function($course) { return $course->category; }, $courseobjs)); foreach ($params['events']['courseids'] as $id) { try { $context = context_course::instance($id); self::validate_context($context); $funcparam['courses'][] = $id; } catch (Exception $e) { $warnings[] = array( 'item' => 'course', 'itemid' => $id, 'warningcode' => 'nopermissions', 'message' => 'No access rights in course context '.$e->getMessage().$e->getTraceAsString() ); } } } else { $courses = $params['events']['courseids']; $funcparam['courses'] = $courses; if (!empty($courses)) { list($wheresql, $sqlparams) = $DB->get_in_or_equal($courses); $wheresql = "id $wheresql"; $coursecategories = array_flip(array_map(function($course) { return $course->category; }, $DB->get_records_select('course', $wheresql, $sqlparams, '', 'category'))); } } // Let us findout groups that we can return events from. if (!$hassystemcap) { $groups = groups_get_my_groups(); $groups = array_keys($groups); foreach ($params['events']['groupids'] as $id) { if (in_array($id, $groups)) { $funcparam['groups'][] = $id; } else { $warnings[] = array('item' => $id, 'warningcode' => 'nopermissions', 'message' => 'you do not have permissions to access this group'); } } } else { $groups = $params['events']['groupids']; $funcparam['groups'] = $groups; } $categories = array(); if ($hassystemcap || !empty($courses)) { // Use the category id as the key in the following array. That way we do not have to remove duplicates and // have a faster lookup later. $categories = []; if (!empty($params['events']['categoryids'])) { $catobjs = \core_course_category::get_many( array_merge($params['events']['categoryids'], array_keys($coursecategories))); foreach ($catobjs as $catobj) { if (isset($coursecategories[$catobj->id]) || has_capability('moodle/category:manage', $catobj->get_context())) { // If the user has access to a course in this category or can manage the category, // then they can see all parent categories too. $categories[$catobj->id] = true; foreach ($catobj->get_parents() as $catid) { $categories[$catid] = true; } } } $funcparam['categories'] = array_keys($categories); } else { // Fetch all categories where this user has any enrolment, and all categories that this user can manage. $calcatcache = cache::make('core', 'calendar_categories'); // Do not use cache if the user has the system capability as $coursecategories might not represent the // courses the user is enrolled in. $categories = (!$hassystemcap) ? $calcatcache->get('site') : false; if ($categories !== false) { // The ids are stored in a list in the cache. $funcparam['categories'] = $categories; $categories = array_flip($categories); } else { $categories = []; foreach (\core_course_category::get_all() as $category) { if (isset($coursecategories[$category->id]) || has_capability('moodle/category:manage', $category->get_context(), $USER, false)) { // If the user has access to a course in this category or can manage the category, // then they can see all parent categories too. $categories[$category->id] = true; foreach ($category->get_parents() as $catid) { $categories[$catid] = true; } } } $funcparam['categories'] = array_keys($categories); if (!$hassystemcap) { $calcatcache->set('site', $funcparam['categories']); } } } } // Do we need user events? if (!empty($params['options']['userevents'])) { $funcparam['users'] = array($USER->id); } else { $funcparam['users'] = false; } // Do we need site events? if (!empty($params['options']['siteevents'])) { $funcparam['courses'][] = $SITE->id; } // We treat 0 and null as no end. if (empty($params['options']['timeend'])) { $params['options']['timeend'] = PHP_INT_MAX; } // Event list does not check visibility and permissions, we'll check that later. $eventlist = calendar_get_legacy_events($params['options']['timestart'], $params['options']['timeend'], $funcparam['users'], $funcparam['groups'], $funcparam['courses'], true, $params['options']['ignorehidden'], $funcparam['categories']); // WS expects arrays. $events = array(); // We need to get events asked for eventids. if ($eventsbyid = calendar_get_events_by_id($params['events']['eventids'])) { $eventlist += $eventsbyid; } foreach ($eventlist as $eventid => $eventobj) { $event = (array) $eventobj; // Description formatting. $calendareventobj = new calendar_event($event); $event['name'] = $calendareventobj->format_external_name(); list($event['description'], $event['format']) = $calendareventobj->format_external_text(); if ($hassystemcap) { // User can see everything, no further check is needed. $events[$eventid] = $event; } else if (!empty($eventobj->modulename)) { $courseid = $eventobj->courseid; if (!$courseid) { if (!$calendareventobj->context || !($context = $calendareventobj->context->get_course_context(false))) { continue; } $courseid = $context->instanceid; } $instances = get_fast_modinfo($courseid)->get_instances_of($eventobj->modulename); if (!empty($instances[$eventobj->instance]->uservisible)) { $events[$eventid] = $event; } } else { // Can the user actually see this event? $eventobj = calendar_event::load($eventobj); if ((($eventobj->courseid == $SITE->id) && (empty($eventobj->categoryid))) || (!empty($eventobj->categoryid) && isset($categories[$eventobj->categoryid])) || (!empty($eventobj->groupid) && in_array($eventobj->groupid, $groups)) || (!empty($eventobj->courseid) && in_array($eventobj->courseid, $courses)) || ($USER->id == $eventobj->userid) || (calendar_edit_event_allowed($eventobj))) { $events[$eventid] = $event; } else { $warnings[] = array('item' => $eventid, 'warningcode' => 'nopermissions', 'message' => 'you do not have permissions to view this event'); } } } return array('events' => $events, 'warnings' => $warnings); } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.5 */ public static function get_calendar_events_returns() { return new external_single_structure(array( 'events' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'event id'), 'name' => new external_value(PARAM_RAW, 'event name'), 'description' => new external_value(PARAM_RAW, 'Description', VALUE_OPTIONAL, null, NULL_ALLOWED), 'format' => new external_format_value('description'), 'courseid' => new external_value(PARAM_INT, 'course id'), 'categoryid' => new external_value(PARAM_INT, 'Category id (only for category events).', VALUE_OPTIONAL), 'groupid' => new external_value(PARAM_INT, 'group id'), 'userid' => new external_value(PARAM_INT, 'user id'), 'repeatid' => new external_value(PARAM_INT, 'repeat id'), 'modulename' => new external_value(PARAM_TEXT, 'module name', VALUE_OPTIONAL, null, NULL_ALLOWED), 'instance' => new external_value(PARAM_INT, 'instance id'), 'eventtype' => new external_value(PARAM_TEXT, 'Event type'), 'timestart' => new external_value(PARAM_INT, 'timestart'), 'timeduration' => new external_value(PARAM_INT, 'time duration'), 'visible' => new external_value(PARAM_INT, 'visible'), 'uuid' => new external_value(PARAM_TEXT, 'unique id of ical events', VALUE_OPTIONAL, null, NULL_NOT_ALLOWED), 'sequence' => new external_value(PARAM_INT, 'sequence'), 'timemodified' => new external_value(PARAM_INT, 'time modified'), 'subscriptionid' => new external_value(PARAM_INT, 'Subscription id', VALUE_OPTIONAL, null, NULL_ALLOWED), ), 'event') ), 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters. * * @since Moodle 3.3 * @return external_function_parameters */ public static function get_calendar_action_events_by_timesort_parameters() { return new external_function_parameters( array( 'timesortfrom' => new external_value(PARAM_INT, 'Time sort from', VALUE_DEFAULT, 0), 'timesortto' => new external_value(PARAM_INT, 'Time sort to', VALUE_DEFAULT, null), 'aftereventid' => new external_value(PARAM_INT, 'The last seen event id', VALUE_DEFAULT, 0), 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 20), 'limittononsuspendedevents' => new external_value(PARAM_BOOL, 'Limit the events to courses the user is not suspended in', VALUE_DEFAULT, false), 'userid' => new external_value(PARAM_INT, 'The user id', VALUE_DEFAULT, null), 'searchvalue' => new external_value(PARAM_RAW, 'The value a user wishes to search against', VALUE_DEFAULT, null) ) ); } /** * Get calendar action events based on the timesort value. * * @since Moodle 3.3 * @param null|int $timesortfrom Events after this time (inclusive) * @param null|int $timesortto Events before this time (inclusive) * @param null|int $aftereventid Get events with ids greater than this one * @param int $limitnum Limit the number of results to this value * @param null|int $userid The user id * @param string|null $searchvalue The value a user wishes to search against * @return array */ public static function get_calendar_action_events_by_timesort($timesortfrom = 0, $timesortto = null, $aftereventid = 0, $limitnum = 20, $limittononsuspendedevents = false, $userid = null, ?string $searchvalue = null) { global $PAGE, $USER; $params = self::validate_parameters( self::get_calendar_action_events_by_timesort_parameters(), [ 'timesortfrom' => $timesortfrom, 'timesortto' => $timesortto, 'aftereventid' => $aftereventid, 'limitnum' => $limitnum, 'limittononsuspendedevents' => $limittononsuspendedevents, 'userid' => $userid, 'searchvalue' => $searchvalue ] ); if ($params['userid']) { $user = \core_user::get_user($params['userid']); } else { $user = $USER; } $context = \context_user::instance($user->id); self::validate_context($context); if ($params['userid'] && $USER->id !== $params['userid'] && !has_capability('moodle/calendar:manageentries', $context)) { throw new \required_capability_exception($context, 'moodle/calendar:manageentries', 'nopermission', ''); } if (empty($params['aftereventid'])) { $params['aftereventid'] = null; } $renderer = $PAGE->get_renderer('core_calendar'); $events = local_api::get_action_events_by_timesort( $params['timesortfrom'], $params['timesortto'], $params['aftereventid'], $params['limitnum'], $params['limittononsuspendedevents'], $user, clean_param($params['searchvalue'], PARAM_TEXT) ); $exportercache = new events_related_objects_cache($events); $exporter = new events_exporter($events, ['cache' => $exportercache]); return $exporter->export($renderer); } /** * Returns description of method result value. * * @since Moodle 3.3 * @return \core_external\external_description */ public static function get_calendar_action_events_by_timesort_returns() { return events_exporter::get_read_structure(); } /** * Returns description of method parameters. * * @return external_function_parameters */ public static function get_calendar_action_events_by_course_parameters() { return new external_function_parameters( array( 'courseid' => new external_value(PARAM_INT, 'Course id'), 'timesortfrom' => new external_value(PARAM_INT, 'Time sort from', VALUE_DEFAULT, null), 'timesortto' => new external_value(PARAM_INT, 'Time sort to', VALUE_DEFAULT, null), 'aftereventid' => new external_value(PARAM_INT, 'The last seen event id', VALUE_DEFAULT, 0), 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 20), 'searchvalue' => new external_value(PARAM_RAW, 'The value a user wishes to search against', VALUE_DEFAULT, null) ) ); } /** * Get calendar action events for the given course. * * @since Moodle 3.3 * @param int $courseid Only events in this course * @param null|int $timesortfrom Events after this time (inclusive) * @param null|int $timesortto Events before this time (inclusive) * @param null|int $aftereventid Get events with ids greater than this one * @param int $limitnum Limit the number of results to this value * @param string|null $searchvalue The value a user wishes to search against * @return array */ public static function get_calendar_action_events_by_course( $courseid, $timesortfrom = null, $timesortto = null, $aftereventid = 0, $limitnum = 20, ?string $searchvalue = null) { global $PAGE, $USER; $user = null; $params = self::validate_parameters( self::get_calendar_action_events_by_course_parameters(), [ 'courseid' => $courseid, 'timesortfrom' => $timesortfrom, 'timesortto' => $timesortto, 'aftereventid' => $aftereventid, 'limitnum' => $limitnum, 'searchvalue' => $searchvalue ] ); $context = \context_user::instance($USER->id); self::validate_context($context); if (empty($params['aftereventid'])) { $params['aftereventid'] = null; } $courses = enrol_get_my_courses('*', null, 0, [$courseid]); $courses = array_values($courses); if (empty($courses)) { return []; } $course = $courses[0]; $renderer = $PAGE->get_renderer('core_calendar'); $events = local_api::get_action_events_by_course( $course, $params['timesortfrom'], $params['timesortto'], $params['aftereventid'], $params['limitnum'], clean_param($params['searchvalue'], PARAM_TEXT) ); $exportercache = new events_related_objects_cache($events, $courses); $exporter = new events_exporter($events, ['cache' => $exportercache]); return $exporter->export($renderer); } /** * Returns description of method result value. * * @return \core_external\external_description */ public static function get_calendar_action_events_by_course_returns() { return events_exporter::get_read_structure(); } /** * Returns description of method parameters. * * @return external_function_parameters */ public static function get_calendar_action_events_by_courses_parameters() { return new external_function_parameters( array( 'courseids' => new external_multiple_structure( new external_value(PARAM_INT, 'Course id') ), 'timesortfrom' => new external_value(PARAM_INT, 'Time sort from', VALUE_DEFAULT, null), 'timesortto' => new external_value(PARAM_INT, 'Time sort to', VALUE_DEFAULT, null), 'limitnum' => new external_value(PARAM_INT, 'Limit number', VALUE_DEFAULT, 10), 'searchvalue' => new external_value(PARAM_RAW, 'The value a user wishes to search against', VALUE_DEFAULT, null) ) ); } /** * Get calendar action events for a given list of courses. * * @since Moodle 3.3 * @param array $courseids Only include events for these courses * @param null|int $timesortfrom Events after this time (inclusive) * @param null|int $timesortto Events before this time (inclusive) * @param int $limitnum Limit the number of results per course to this value * @param string|null $searchvalue The value a user wishes to search against * @return array */ public static function get_calendar_action_events_by_courses( array $courseids, $timesortfrom = null, $timesortto = null, $limitnum = 10, ?string $searchvalue = null) { global $PAGE, $USER; $user = null; $params = self::validate_parameters( self::get_calendar_action_events_by_courses_parameters(), [ 'courseids' => $courseids, 'timesortfrom' => $timesortfrom, 'timesortto' => $timesortto, 'limitnum' => $limitnum, 'searchvalue' => $searchvalue ] ); $context = \context_user::instance($USER->id); self::validate_context($context); if (empty($params['courseids'])) { return ['groupedbycourse' => []]; } $renderer = $PAGE->get_renderer('core_calendar'); $courses = enrol_get_my_courses('*', null, 0, $params['courseids']); $courses = array_values($courses); if (empty($courses)) { return ['groupedbycourse' => []]; } $events = local_api::get_action_events_by_courses( $courses, $params['timesortfrom'], $params['timesortto'], $params['limitnum'], clean_param($params['searchvalue'], PARAM_TEXT) ); if (empty($events)) { return ['groupedbycourse' => []]; } $exportercache = new events_related_objects_cache($events, $courses); $exporter = new events_grouped_by_course_exporter($events, ['cache' => $exportercache]); return $exporter->export($renderer); } /** * Returns description of method result value. * * @return \core_external\external_description */ public static function get_calendar_action_events_by_courses_returns() { return events_grouped_by_course_exporter::get_read_structure(); } /** * Returns description of method parameters. * * @return external_function_parameters. * @since Moodle 2.5 */ public static function create_calendar_events_parameters() { // Userid is always current user, so no need to get it from client. // Module based calendar events are not allowed here. Hence no need of instance and modulename. // subscription id and uuid is not allowed as this is not an ical api. return new external_function_parameters( array('events' => new external_multiple_structure( new external_single_structure( array( 'name' => new external_value(PARAM_TEXT, 'event name', VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'description' => new external_value(PARAM_RAW, 'Description', VALUE_DEFAULT, null, NULL_ALLOWED), 'format' => new external_format_value('description', VALUE_DEFAULT), 'courseid' => new external_value(PARAM_INT, 'course id', VALUE_DEFAULT, 0, NULL_NOT_ALLOWED), 'groupid' => new external_value(PARAM_INT, 'group id', VALUE_DEFAULT, 0, NULL_NOT_ALLOWED), 'repeats' => new external_value(PARAM_INT, 'number of repeats', VALUE_DEFAULT, 0, NULL_NOT_ALLOWED), 'eventtype' => new external_value(PARAM_TEXT, 'Event type', VALUE_DEFAULT, 'user', NULL_NOT_ALLOWED), 'timestart' => new external_value(PARAM_INT, 'timestart', VALUE_DEFAULT, time(), NULL_NOT_ALLOWED), 'timeduration' => new external_value(PARAM_INT, 'time duration', VALUE_DEFAULT, 0, NULL_NOT_ALLOWED), 'visible' => new external_value(PARAM_INT, 'visible', VALUE_DEFAULT, 1, NULL_NOT_ALLOWED), 'sequence' => new external_value(PARAM_INT, 'sequence', VALUE_DEFAULT, 1, NULL_NOT_ALLOWED), ), 'event') ) ) ); } /** * Create calendar events. * * @param array $events A list of events to create. * @return array array of events created. * @since Moodle 2.5 * @throws moodle_exception if user doesnt have the permission to create events. */ public static function create_calendar_events($events) { global $DB, $USER; // Parameter validation. $params = self::validate_parameters(self::create_calendar_events_parameters(), array('events' => $events)); $transaction = $DB->start_delegated_transaction(); $return = array(); $warnings = array(); foreach ($params['events'] as $event) { // Let us set some defaults. $event['userid'] = $USER->id; $event['modulename'] = ''; $event['instance'] = 0; $event['subscriptionid'] = null; $event['uuid']= ''; $event['format'] = external_util::validate_format($event['format']); if ($event['repeats'] > 0) { $event['repeat'] = 1; } else { $event['repeat'] = 0; } $eventobj = new calendar_event($event); // Let's check if the user is allowed to delete an event. if (!calendar_add_event_allowed($eventobj)) { $warnings [] = array('item' => $event['name'], 'warningcode' => 'nopermissions', 'message' => 'you do not have permissions to create this event'); continue; } // Let's create the event. $var = $eventobj->create($event); $var = (array)$var->properties(); if ($event['repeat']) { $children = $DB->get_records('event', array('repeatid' => $var['id'])); foreach ($children as $child) { $return[] = (array) $child; } } else { $return[] = $var; } } // Everything done smoothly, let's commit. $transaction->allow_commit(); return array('events' => $return, 'warnings' => $warnings); } /** * Returns description of method result value. * * @return \core_external\external_description. * @since Moodle 2.5 */ public static function create_calendar_events_returns() { return new external_single_structure( array( 'events' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'event id'), 'name' => new external_value(PARAM_RAW, 'event name'), 'description' => new external_value(PARAM_RAW, 'Description', VALUE_OPTIONAL), 'format' => new external_format_value('description'), 'courseid' => new external_value(PARAM_INT, 'course id'), 'groupid' => new external_value(PARAM_INT, 'group id'), 'userid' => new external_value(PARAM_INT, 'user id'), 'repeatid' => new external_value(PARAM_INT, 'repeat id', VALUE_OPTIONAL), 'modulename' => new external_value(PARAM_TEXT, 'module name', VALUE_OPTIONAL), 'instance' => new external_value(PARAM_INT, 'instance id'), 'eventtype' => new external_value(PARAM_TEXT, 'Event type'), 'timestart' => new external_value(PARAM_INT, 'timestart'), 'timeduration' => new external_value(PARAM_INT, 'time duration'), 'visible' => new external_value(PARAM_INT, 'visible'), 'uuid' => new external_value(PARAM_TEXT, 'unique id of ical events', VALUE_OPTIONAL, '', NULL_NOT_ALLOWED), 'sequence' => new external_value(PARAM_INT, 'sequence'), 'timemodified' => new external_value(PARAM_INT, 'time modified'), 'subscriptionid' => new external_value(PARAM_INT, 'Subscription id', VALUE_OPTIONAL), ), 'event') ), 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters. * * @return external_function_parameters */ public static function get_calendar_event_by_id_parameters() { return new external_function_parameters( array( 'eventid' => new external_value(PARAM_INT, 'The event id to be retrieved'), ) ); } /** * Get calendar event by id. * * @param int $eventid The calendar event id to be retrieved. * @return array Array of event details */ public static function get_calendar_event_by_id($eventid) { global $PAGE, $USER; $params = self::validate_parameters(self::get_calendar_event_by_id_parameters(), ['eventid' => $eventid]); $context = \context_user::instance($USER->id); self::validate_context($context); $warnings = array(); $eventvault = event_container::get_event_vault(); if ($event = $eventvault->get_event_by_id($params['eventid'])) { $mapper = event_container::get_event_mapper(); if (!calendar_view_event_allowed($mapper->from_event_to_legacy_event($event))) { throw new moodle_exception('nopermissiontoviewcalendar', 'error'); } } if (!$event) { // We can't return a warning in this case because the event is not optional. // We don't know the context for the event and it's not worth loading it. $syscontext = context_system::instance(); throw new \required_capability_exception($syscontext, 'moodle/course:view', 'nopermissions', 'error'); } $cache = new events_related_objects_cache([$event]); $relatedobjects = [ 'context' => $cache->get_context($event), 'course' => $cache->get_course($event), ]; $exporter = new event_exporter($event, $relatedobjects); $renderer = $PAGE->get_renderer('core_calendar'); return array('event' => $exporter->export($renderer), 'warnings' => $warnings); } /** * Returns description of method result value * * @return \core_external\external_description */ public static function get_calendar_event_by_id_returns() { $eventstructure = event_exporter::get_read_structure(); return new external_single_structure(array( 'event' => $eventstructure, 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters. * * @return external_function_parameters. */ public static function submit_create_update_form_parameters() { return new external_function_parameters( [ 'formdata' => new external_value(PARAM_RAW, 'The data from the event form'), ] ); } /** * Handles the event form submission. * * @param string $formdata The event form data in a URI encoded param string * @return array The created or modified event * @throws moodle_exception */ public static function submit_create_update_form($formdata) { global $USER, $PAGE, $CFG; require_once($CFG->libdir."/filelib.php"); // Parameter validation. $params = self::validate_parameters(self::submit_create_update_form_parameters(), ['formdata' => $formdata]); $context = \context_user::instance($USER->id); $data = []; self::validate_context($context); parse_str($params['formdata'], $data); if (WS_SERVER) { // Request via WS, ignore sesskey checks in form library. $USER->ignoresesskey = true; } $eventtype = isset($data['eventtype']) ? $data['eventtype'] : null; $coursekey = ($eventtype == 'group') ? 'groupcourseid' : 'courseid'; $courseid = (!empty($data[$coursekey])) ? $data[$coursekey] : null; $editoroptions = \core_calendar\local\event\forms\create::build_editor_options($context); $formoptions = ['editoroptions' => $editoroptions, 'courseid' => $courseid]; $allowedeeventtypes = calendar_get_allowed_event_types($courseid); // Event type validation. if (in_array(true, $allowedeeventtypes, true) === false) { throw new \moodle_exception('nopermissiontoupdatecalendar'); } if (empty($eventtype) || !isset($allowedeeventtypes[$eventtype]) || $allowedeeventtypes[$eventtype] == false) { return ['validationerror' => true]; } $formoptions['eventtypes'] = $allowedeeventtypes; if ($courseid) { require_once($CFG->libdir . '/grouplib.php'); $groupcoursedata = groups_get_all_groups($courseid); if (!empty($groupcoursedata)) { $formoptions['groups'] = []; foreach ($groupcoursedata as $groupid => $groupdata) { $formoptions['groups'][$groupid] = $groupdata->name; } } } if (!empty($data['id'])) { $eventid = clean_param($data['id'], PARAM_INT); $legacyevent = calendar_event::load($eventid); $legacyevent->count_repeats(); $formoptions['event'] = $legacyevent; $mform = new update_event_form(null, $formoptions, 'post', '', null, true, $data); } else { $legacyevent = null; $mform = new create_event_form(null, $formoptions, 'post', '', null, true, $data); } if ($validateddata = $mform->get_data()) { $formmapper = new create_update_form_mapper(); $properties = $formmapper->from_data_to_event_properties($validateddata); if (is_null($legacyevent)) { $legacyevent = new \calendar_event($properties); // Need to do this in order to initialise the description // property which then triggers the update function below // to set the appropriate default properties on the event. $properties = $legacyevent->properties(true); } if (!calendar_edit_event_allowed($legacyevent, true)) { throw new \moodle_exception('nopermissiontoupdatecalendar'); } $legacyevent->update($properties); $eventcontext = $legacyevent->context; file_remove_editor_orphaned_files($validateddata->description); // Take any files added to the description draft file area and // convert them into the proper event description file area. Also // parse the description text and replace the URLs to the draft files // with the @@PLUGIN_FILE@@ placeholder to be persisted in the DB. $description = file_save_draft_area_files( $validateddata->description['itemid'], $eventcontext->id, 'calendar', 'event_description', $legacyevent->id, create_event_form::build_editor_options($eventcontext), $validateddata->description['text'] ); // If draft files were found then we need to save the new // description value. if ($description != $validateddata->description['text']) { $properties->id = $legacyevent->id; $properties->description = $description; $legacyevent->update($properties); } $eventmapper = event_container::get_event_mapper(); $event = $eventmapper->from_legacy_event_to_event($legacyevent); $cache = new events_related_objects_cache([$event]); $relatedobjects = [ 'context' => $cache->get_context($event), 'course' => $cache->get_course($event), ]; $exporter = new event_exporter($event, $relatedobjects); $renderer = $PAGE->get_renderer('core_calendar'); return [ 'event' => $exporter->export($renderer) ]; } else { return [ 'validationerror' => true ]; } } /** * Returns description of method result value. * * @return \core_external\external_description. */ public static function submit_create_update_form_returns() { $eventstructure = event_exporter::get_read_structure(); $eventstructure->required = VALUE_OPTIONAL; return new external_single_structure( array( 'event' => $eventstructure, 'validationerror' => new external_value(PARAM_BOOL, 'Invalid form data', VALUE_DEFAULT, false), ) ); } /** * Get data for the monthly calendar view. * * @param int $year The year to be shown * @param int $month The month to be shown * @param int $courseid The course to be included * @param int $categoryid The category to be included * @param bool $includenavigation Whether to include navigation * @param bool $mini Whether to return the mini month view or not * @param int $day The day we want to keep as the current day * @param string|null $view The view mode for the calendar. * @return array */ public static function get_calendar_monthly_view($year, $month, $courseid, $categoryid, $includenavigation, $mini, $day, ?string $view = null) { global $USER, $PAGE; // Parameter validation. $params = self::validate_parameters(self::get_calendar_monthly_view_parameters(), [ 'year' => $year, 'month' => $month, 'courseid' => $courseid, 'categoryid' => $categoryid, 'includenavigation' => $includenavigation, 'mini' => $mini, 'day' => $day, 'view' => $view, ]); $context = \context_user::instance($USER->id); self::validate_context($context); $PAGE->set_url('/calendar/'); $type = \core_calendar\type_factory::get_calendar_instance(); $time = $type->convert_to_timestamp($params['year'], $params['month'], $params['day']); $calendar = \calendar_information::create($time, $params['courseid'], $params['categoryid']); self::validate_context($calendar->context); $view = $params['view'] ?? ($params['mini'] ? 'mini' : 'month'); list($data, $template) = calendar_get_view($calendar, $view, $params['includenavigation']); return $data; } /** * Returns description of method parameters. * * @return external_function_parameters */ public static function get_calendar_monthly_view_parameters() { return new external_function_parameters( [ 'year' => new external_value(PARAM_INT, 'Year to be viewed', VALUE_REQUIRED), 'month' => new external_value(PARAM_INT, 'Month to be viewed', VALUE_REQUIRED), 'courseid' => new external_value(PARAM_INT, 'Course being viewed', VALUE_DEFAULT, SITEID, NULL_ALLOWED), 'categoryid' => new external_value(PARAM_INT, 'Category being viewed', VALUE_DEFAULT, null, NULL_ALLOWED), 'includenavigation' => new external_value( PARAM_BOOL, 'Whether to show course navigation', VALUE_DEFAULT, true, NULL_ALLOWED ), 'mini' => new external_value( PARAM_BOOL, 'Whether to return the mini month view or not', VALUE_DEFAULT, false, NULL_ALLOWED ), 'day' => new external_value(PARAM_INT, 'Day to be viewed', VALUE_DEFAULT, 1), 'view' => new external_value(PARAM_ALPHA, 'The view mode of the calendar', VALUE_DEFAULT, 'month', NULL_ALLOWED), ] ); } /** * Returns description of method result value. * * @return \core_external\external_description */ public static function get_calendar_monthly_view_returns() { return \core_calendar\external\month_exporter::get_read_structure(); } /** * Get data for the daily calendar view. * * @param int $year The year to be shown * @param int $month The month to be shown * @param int $day The day to be shown * @param int $courseid The course to be included * @return array */ public static function get_calendar_day_view($year, $month, $day, $courseid, $categoryid) { global $DB, $USER, $PAGE; // Parameter validation. $params = self::validate_parameters(self::get_calendar_day_view_parameters(), [ 'year' => $year, 'month' => $month, 'day' => $day, 'courseid' => $courseid, 'categoryid' => $categoryid, ]); $context = \context_user::instance($USER->id); self::validate_context($context); $type = \core_calendar\type_factory::get_calendar_instance(); $time = $type->convert_to_timestamp($params['year'], $params['month'], $params['day']); $calendar = \calendar_information::create($time, $params['courseid'], $params['categoryid']); self::validate_context($calendar->context); list($data, $template) = calendar_get_view($calendar, 'day'); return $data; } /** * Returns description of method parameters. * * @return external_function_parameters */ public static function get_calendar_day_view_parameters() { return new external_function_parameters( [ 'year' => new external_value(PARAM_INT, 'Year to be viewed', VALUE_REQUIRED), 'month' => new external_value(PARAM_INT, 'Month to be viewed', VALUE_REQUIRED), 'day' => new external_value(PARAM_INT, 'Day to be viewed', VALUE_REQUIRED), 'courseid' => new external_value(PARAM_INT, 'Course being viewed', VALUE_DEFAULT, SITEID, NULL_ALLOWED), 'categoryid' => new external_value(PARAM_INT, 'Category being viewed', VALUE_DEFAULT, null, NULL_ALLOWED), ] ); } /** * Returns description of method result value. * * @return \core_external\external_description */ public static function get_calendar_day_view_returns() { return \core_calendar\external\calendar_day_exporter::get_read_structure(); } /** * Returns description of method parameters. * * @return external_function_parameters */ public static function update_event_start_day_parameters() { return new external_function_parameters( [ 'eventid' => new external_value(PARAM_INT, 'Id of event to be updated', VALUE_REQUIRED), 'daytimestamp' => new external_value(PARAM_INT, 'Timestamp for the new start day', VALUE_REQUIRED), ] ); } /** * Change the start day for the given calendar event to the day that * corresponds with the provided timestamp. * * The timestamp only needs to be anytime within the desired day as only * the date data is extracted from it. * * The event's original time of day is maintained, only the date is shifted. * * @param int $eventid Id of event to be updated * @param int $daytimestamp Timestamp for the new start day * @return array */ public static function update_event_start_day($eventid, $daytimestamp) { global $USER, $PAGE; // Parameter validation. $params = self::validate_parameters(self::update_event_start_day_parameters(), [ 'eventid' => $eventid, 'daytimestamp' => $daytimestamp, ]); $vault = event_container::get_event_vault(); $mapper = event_container::get_event_mapper(); $event = $vault->get_event_by_id($eventid); if (!$event) { throw new \moodle_exception('Unable to find event with id ' . $eventid); } $legacyevent = $mapper->from_event_to_legacy_event($event); if (!calendar_edit_event_allowed($legacyevent, true)) { throw new \moodle_exception('nopermissiontoupdatecalendar'); } self::validate_context($legacyevent->context); $newdate = usergetdate($daytimestamp); $startdatestring = implode('-', [$newdate['year'], $newdate['mon'], $newdate['mday']]); $startdate = new DateTimeImmutable($startdatestring); $event = local_api::update_event_start_day($event, $startdate); $cache = new events_related_objects_cache([$event]); $relatedobjects = [ 'context' => $cache->get_context($event), 'course' => $cache->get_course($event), ]; $exporter = new event_exporter($event, $relatedobjects); $renderer = $PAGE->get_renderer('core_calendar'); return array('event' => $exporter->export($renderer)); } /** * Returns description of method result value. * * @return \core_external\external_description */ public static function update_event_start_day_returns() { return new external_single_structure( array( 'event' => event_exporter::get_read_structure() ) ); } /** * Get data for the monthly calendar view. * * @param int $courseid The course to be included * @param int $categoryid The category to be included * @return array */ public static function get_calendar_upcoming_view($courseid, $categoryid) { global $DB, $USER, $PAGE; // Parameter validation. $params = self::validate_parameters(self::get_calendar_upcoming_view_parameters(), [ 'courseid' => $courseid, 'categoryid' => $categoryid, ]); $context = \context_user::instance($USER->id); self::validate_context($context); $PAGE->set_url('/calendar/'); $calendar = \calendar_information::create(time(), $params['courseid'], $params['categoryid']); self::validate_context($calendar->context); list($data, $template) = calendar_get_view($calendar, 'upcoming'); return $data; } /** * Returns description of method parameters. * * @return external_function_parameters */ public static function get_calendar_upcoming_view_parameters() { return new external_function_parameters( [ 'courseid' => new external_value(PARAM_INT, 'Course being viewed', VALUE_DEFAULT, SITEID, NULL_ALLOWED), 'categoryid' => new external_value(PARAM_INT, 'Category being viewed', VALUE_DEFAULT, null, NULL_ALLOWED), ] ); } /** * Returns description of method result value. * * @return \core_external\external_description */ public static function get_calendar_upcoming_view_returns() { return \core_calendar\external\calendar_upcoming_exporter::get_read_structure(); } /** * Returns description of method parameters. * * @return external_function_parameters. * @since Moodle 3.7 */ public static function get_calendar_access_information_parameters() { return new external_function_parameters( [ 'courseid' => new external_value(PARAM_INT, 'Course to check, empty for site calendar events.', VALUE_DEFAULT, 0), ] ); } /** * Convenience function to retrieve some permissions information for the given course calendar. * * @param int $courseid Course to check, empty for site. * @return array The access information * @throws moodle_exception * @since Moodle 3.7 */ public static function get_calendar_access_information($courseid = 0) { $params = self::validate_parameters(self::get_calendar_access_information_parameters(), ['courseid' => $courseid]); if (empty($params['courseid']) || $params['courseid'] == SITEID) { $context = \context_system::instance(); } else { $context = \context_course::instance($params['courseid']); } self::validate_context($context); return [ 'canmanageentries' => has_capability('moodle/calendar:manageentries', $context), 'canmanageownentries' => has_capability('moodle/calendar:manageownentries', $context), 'canmanagegroupentries' => has_capability('moodle/calendar:managegroupentries', $context), 'warnings' => [], ]; } /** * Returns description of method result value. * * @return \core_external\external_description. * @since Moodle 3.7 */ public static function get_calendar_access_information_returns() { return new external_single_structure( [ 'canmanageentries' => new external_value(PARAM_BOOL, 'Whether the user can manage entries.'), 'canmanageownentries' => new external_value(PARAM_BOOL, 'Whether the user can manage its own entries.'), 'canmanagegroupentries' => new external_value(PARAM_BOOL, 'Whether the user can manage group entries.'), 'warnings' => new external_warnings(), ] ); } /** * Returns description of method parameters. * * @return external_function_parameters. * @since Moodle 3.7 */ public static function get_allowed_event_types_parameters() { return new external_function_parameters( [ 'courseid' => new external_value(PARAM_INT, 'Course to check, empty for site.', VALUE_DEFAULT, 0), ] ); } /** * Get the type of events a user can create in the given course. * * @param int $courseid Course to check, empty for site. * @return array The types allowed * @throws moodle_exception * @since Moodle 3.7 */ public static function get_allowed_event_types($courseid = 0) { $params = self::validate_parameters(self::get_allowed_event_types_parameters(), ['courseid' => $courseid]); if (empty($params['courseid']) || $params['courseid'] == SITEID) { $context = \context_system::instance(); } else { $context = \context_course::instance($params['courseid']); } self::validate_context($context); $allowedeventtypes = array_filter(calendar_get_allowed_event_types($params['courseid'])); return [ 'allowedeventtypes' => array_keys($allowedeventtypes), 'warnings' => [], ]; } /** * Returns description of method result value. * * @return \core_external\external_description. * @since Moodle 3.7 */ public static function get_allowed_event_types_returns() { return new external_single_structure( [ 'allowedeventtypes' => new external_multiple_structure( new external_value(PARAM_NOTAGS, 'Allowed event types to be created in the given course.') ), 'warnings' => new external_warnings(), ] ); } /** * Convert the specified dates into unix timestamps. * * @param array $datetimes Array of arrays containing date time details, each in the format: * ['year' => a, 'month' => b, 'day' => c, * 'hour' => d (optional), 'minute' => e (optional), 'key' => 'x' (optional)] * @return array Provided array of dates converted to unix timestamps * @throws moodle_exception If one or more of the dates provided does not convert to a valid timestamp. */ public static function get_timestamps($datetimes) { $params = self::validate_parameters(self::get_timestamps_parameters(), ['data' => $datetimes]); $type = \core_calendar\type_factory::get_calendar_instance(); $timestamps = ['timestamps' => []]; foreach ($params['data'] as $key => $datetime) { $hour = $datetime['hour'] ?? 0; $minute = $datetime['minute'] ?? 0; try { $timestamp = $type->convert_to_timestamp( $datetime['year'], $datetime['month'], $datetime['day'], $hour, $minute); $timestamps['timestamps'][] = [ 'key' => $datetime['key'] ?? $key, 'timestamp' => $timestamp, ]; } catch (Exception $e) { throw new moodle_exception('One or more of the dates provided were invalid'); } } return $timestamps; } /** * Describes the parameters for get_timestamps. * * @return external_function_parameters */ public static function get_timestamps_parameters() { return new external_function_parameters ([ 'data' => new external_multiple_structure( new external_single_structure( [ 'key' => new external_value(PARAM_ALPHANUMEXT, 'key', VALUE_OPTIONAL), 'year' => new external_value(PARAM_INT, 'year'), 'month' => new external_value(PARAM_INT, 'month'), 'day' => new external_value(PARAM_INT, 'day'), 'hour' => new external_value(PARAM_INT, 'hour', VALUE_OPTIONAL), 'minute' => new external_value(PARAM_INT, 'minute', VALUE_OPTIONAL), ] ) ) ]); } /** * Describes the timestamps return format. * * @return external_single_structure */ public static function get_timestamps_returns() { return new external_single_structure( [ 'timestamps' => new external_multiple_structure( new external_single_structure( [ 'key' => new external_value(PARAM_ALPHANUMEXT, 'Timestamp key'), 'timestamp' => new external_value(PARAM_INT, 'Unix timestamp'), ] ) ) ] ); } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/user/externallib.php000064400000255363152266567670022725 0ustar00. use core_external\external_description; use core_external\external_value; use core_external\external_format_value; use core_external\external_single_structure; use core_external\external_multiple_structure; use core_external\external_function_parameters; use core_external\external_warnings; /** * User external functions * * @package core_user * @category external * @copyright 2011 Jerome Mouneyrac * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.2 */ class core_user_external extends \core_external\external_api { /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.2 */ public static function create_users_parameters() { global $CFG; $userfields = [ 'createpassword' => new external_value(PARAM_BOOL, 'True if password should be created and mailed to user.', VALUE_OPTIONAL), // General. 'username' => new external_value(core_user::get_property_type('username'), 'Username policy is defined in Moodle security config.'), 'auth' => new external_value(core_user::get_property_type('auth'), 'Auth plugins include manual, ldap, etc', VALUE_DEFAULT, 'manual', core_user::get_property_null('auth')), 'password' => new external_value(core_user::get_property_type('password'), 'Plain text password consisting of any characters', VALUE_OPTIONAL), 'firstname' => new external_value(core_user::get_property_type('firstname'), 'The first name(s) of the user'), 'lastname' => new external_value(core_user::get_property_type('lastname'), 'The family name of the user'), 'email' => new external_value(core_user::get_property_type('email'), 'A valid and unique email address'), 'maildisplay' => new external_value(core_user::get_property_type('maildisplay'), 'Email visibility', VALUE_OPTIONAL), 'city' => new external_value(core_user::get_property_type('city'), 'Home city of the user', VALUE_OPTIONAL), 'country' => new external_value(core_user::get_property_type('country'), 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL), 'timezone' => new external_value(core_user::get_property_type('timezone'), 'Timezone code such as Australia/Perth, or 99 for default', VALUE_OPTIONAL), 'description' => new external_value(core_user::get_property_type('description'), 'User profile description, no HTML', VALUE_OPTIONAL), // Additional names. 'firstnamephonetic' => new external_value(core_user::get_property_type('firstnamephonetic'), 'The first name(s) phonetically of the user', VALUE_OPTIONAL), 'lastnamephonetic' => new external_value(core_user::get_property_type('lastnamephonetic'), 'The family name phonetically of the user', VALUE_OPTIONAL), 'middlename' => new external_value(core_user::get_property_type('middlename'), 'The middle name of the user', VALUE_OPTIONAL), 'alternatename' => new external_value(core_user::get_property_type('alternatename'), 'The alternate name of the user', VALUE_OPTIONAL), // Interests. 'interests' => new external_value(PARAM_TEXT, 'User interests (separated by commas)', VALUE_OPTIONAL), // Optional. 'idnumber' => new external_value(core_user::get_property_type('idnumber'), 'An arbitrary ID code number perhaps from the institution', VALUE_DEFAULT, ''), 'institution' => new external_value(core_user::get_property_type('institution'), 'institution', VALUE_OPTIONAL), 'department' => new external_value(core_user::get_property_type('department'), 'department', VALUE_OPTIONAL), 'phone1' => new external_value(core_user::get_property_type('phone1'), 'Phone 1', VALUE_OPTIONAL), 'phone2' => new external_value(core_user::get_property_type('phone2'), 'Phone 2', VALUE_OPTIONAL), 'address' => new external_value(core_user::get_property_type('address'), 'Postal address', VALUE_OPTIONAL), // Other user preferences stored in the user table. 'lang' => new external_value(core_user::get_property_type('lang'), 'Language code such as "en", must exist on server', VALUE_DEFAULT, core_user::get_property_default('lang'), core_user::get_property_null('lang')), 'calendartype' => new external_value(core_user::get_property_type('calendartype'), 'Calendar type such as "gregorian", must exist on server', VALUE_DEFAULT, $CFG->calendartype, VALUE_OPTIONAL), 'theme' => new external_value(core_user::get_property_type('theme'), 'Theme name such as "standard", must exist on server', VALUE_OPTIONAL), 'mailformat' => new external_value(core_user::get_property_type('mailformat'), 'Mail format code is 0 for plain text, 1 for HTML etc', VALUE_OPTIONAL), // Custom user profile fields. 'customfields' => new external_multiple_structure( new external_single_structure( [ 'type' => new external_value(PARAM_ALPHANUMEXT, 'The name of the custom field'), 'value' => new external_value(PARAM_RAW, 'The value of the custom field') ] ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL), // User preferences. 'preferences' => new external_multiple_structure( new external_single_structure( [ 'type' => new external_value(PARAM_RAW, 'The name of the preference'), 'value' => new external_value(PARAM_RAW, 'The value of the preference') ] ), 'User preferences', VALUE_OPTIONAL), ]; return new external_function_parameters( [ 'users' => new external_multiple_structure( new external_single_structure($userfields) ) ] ); } /** * Create one or more users. * * @throws invalid_parameter_exception * @param array $users An array of users to create. * @return array An array of arrays * @since Moodle 2.2 */ public static function create_users($users) { global $CFG, $DB; require_once($CFG->dirroot."/lib/weblib.php"); require_once($CFG->dirroot."/user/lib.php"); require_once($CFG->dirroot."/user/editlib.php"); require_once($CFG->dirroot."/user/profile/lib.php"); // Required for customfields related function. // Ensure the current user is allowed to run this function. $context = context_system::instance(); self::validate_context($context); require_capability('moodle/user:create', $context); // Do basic automatic PARAM checks on incoming data, using params description. // If any problems are found then exceptions are thrown with helpful error messages. $params = self::validate_parameters(self::create_users_parameters(), array('users' => $users)); $availableauths = core_component::get_plugin_list('auth'); unset($availableauths['mnet']); // These would need mnethostid too. unset($availableauths['webservice']); // We do not want new webservice users for now. $availablethemes = core_component::get_plugin_list('theme'); $availablelangs = get_string_manager()->get_list_of_translations(); $transaction = $DB->start_delegated_transaction(); $userids = array(); $newpasswordusers = []; foreach ($params['users'] as $user) { // Make sure that the username, firstname and lastname are not blank. foreach (array('username', 'firstname', 'lastname') as $fieldname) { if (trim($user[$fieldname]) === '') { throw new invalid_parameter_exception('The field '.$fieldname.' cannot be blank'); } } // Make sure that the username doesn't already exist. if ($DB->record_exists('user', array('username' => $user['username'], 'mnethostid' => $CFG->mnet_localhost_id))) { throw new invalid_parameter_exception('Username already exists: '.$user['username']); } // Make sure auth is valid. if (empty($availableauths[$user['auth']])) { throw new invalid_parameter_exception('Invalid authentication type: '.$user['auth']); } // Make sure lang is valid. if (empty($availablelangs[$user['lang']])) { throw new invalid_parameter_exception('Invalid language code: '.$user['lang']); } // Make sure lang is valid. if (!empty($user['theme']) && empty($availablethemes[$user['theme']])) { // Theme is VALUE_OPTIONAL, // so no default value // We need to test if the client sent it // => !empty($user['theme']). throw new invalid_parameter_exception('Invalid theme: '.$user['theme']); } // Make sure we have a password or have to create one. $authplugin = get_auth_plugin($user['auth']); if ($authplugin->is_internal() && empty($user['password']) && empty($user['createpassword'])) { throw new invalid_parameter_exception('Invalid password: you must provide a password, or set createpassword.'); } $user['confirmed'] = true; $user['mnethostid'] = $CFG->mnet_localhost_id; // Start of user info validation. // Make sure we validate current user info as handled by current GUI. See user/editadvanced_form.php func validation(). if (!validate_email($user['email'])) { throw new invalid_parameter_exception('Email address is invalid: '.$user['email']); } else if (empty($CFG->allowaccountssameemail)) { // Make a case-insensitive query for the given email address. $select = $DB->sql_equal('email', ':email', false) . ' AND mnethostid = :mnethostid'; $params = array( 'email' => $user['email'], 'mnethostid' => $user['mnethostid'] ); // If there are other user(s) that already have the same email, throw an error. if ($DB->record_exists_select('user', $select, $params)) { throw new invalid_parameter_exception('Email address already exists: '.$user['email']); } } // End of user info validation. $createpassword = !empty($user['createpassword']); unset($user['createpassword']); $updatepassword = false; if ($authplugin->is_internal()) { if ($createpassword) { $user['password'] = ''; } else { $updatepassword = true; } } else { $user['password'] = AUTH_PASSWORD_NOT_CACHED; } // Create the user data now! $user['id'] = user_create_user($user, $updatepassword, false); $userobject = (object)$user; // Set user interests. if (!empty($user['interests'])) { $trimmedinterests = array_map('trim', explode(',', $user['interests'])); $interests = array_filter($trimmedinterests, function($value) { return !empty($value); }); useredit_update_interests($userobject, $interests); } // Custom fields. if (!empty($user['customfields'])) { foreach ($user['customfields'] as $customfield) { // Profile_save_data() saves profile file it's expecting a user with the correct id, // and custom field to be named profile_field_"shortname". $user["profile_field_".$customfield['type']] = $customfield['value']; } profile_save_data((object) $user); } if ($createpassword) { unset_user_preference('create_password', $userobject); set_user_preference('auth_forcepasswordchange', 1, $userobject); // Queue this user for email notification after user creation is finalized. $newpasswordusers[] = $userobject; } // Trigger event. \core\event\user_created::create_from_userid($user['id'])->trigger(); // Preferences. if (!empty($user['preferences'])) { $userpref = (object)$user; foreach ($user['preferences'] as $preference) { $userpref->{'preference_'.$preference['type']} = $preference['value']; } useredit_update_user_preference($userpref); } $userids[] = array('id' => $user['id'], 'username' => $user['username']); } $transaction->allow_commit(); // After database commit, send password emails to queued users. foreach ($newpasswordusers as $userobject) { setnew_password_and_mail($userobject); } return $userids; } /** * Returns description of method result value * * @return external_description * @since Moodle 2.2 */ public static function create_users_returns() { return new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(core_user::get_property_type('id'), 'user id'), 'username' => new external_value(core_user::get_property_type('username'), 'user name'), ) ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.2 */ public static function delete_users_parameters() { return new external_function_parameters( array( 'userids' => new external_multiple_structure(new external_value(core_user::get_property_type('id'), 'user ID')), ) ); } /** * Delete users * * @throws moodle_exception * @param array $userids * @return null * @since Moodle 2.2 */ public static function delete_users($userids) { global $CFG, $DB, $USER; require_once($CFG->dirroot."/user/lib.php"); // Ensure the current user is allowed to run this function. $context = context_system::instance(); require_capability('moodle/user:delete', $context); self::validate_context($context); $params = self::validate_parameters(self::delete_users_parameters(), array('userids' => $userids)); $transaction = $DB->start_delegated_transaction(); foreach ($params['userids'] as $userid) { $user = $DB->get_record('user', array('id' => $userid, 'deleted' => 0), '*', MUST_EXIST); // Must not allow deleting of admins or self!!! if (is_siteadmin($user)) { throw new moodle_exception('useradminodelete', 'error'); } if ($USER->id == $user->id) { throw new moodle_exception('usernotdeletederror', 'error'); } user_delete_user($user); } $transaction->allow_commit(); return null; } /** * Returns description of method result value * * @return null * @since Moodle 2.2 */ public static function delete_users_returns() { return null; } /** * Returns description of method parameters. * * @return external_function_parameters * @since Moodle 3.2 */ public static function update_user_preferences_parameters() { return new external_function_parameters( array( 'userid' => new external_value(PARAM_INT, 'id of the user, default to current user', VALUE_DEFAULT, 0), 'emailstop' => new external_value(core_user::get_property_type('emailstop'), 'Enable or disable notifications for this user', VALUE_DEFAULT, null), 'preferences' => new external_multiple_structure( new external_single_structure( array( 'type' => new external_value(PARAM_RAW, 'The name of the preference'), 'value' => new external_value(PARAM_RAW, 'The value of the preference, do not set this field if you want to remove (unset) the current value.', VALUE_DEFAULT, null), ) ), 'User preferences', VALUE_DEFAULT, array() ) ) ); } /** * Update the user's preferences. * * @param int $userid * @param bool|null $emailstop * @param array $preferences * @return null * @since Moodle 3.2 */ public static function update_user_preferences($userid = 0, $emailstop = null, $preferences = array()) { global $USER, $CFG; require_once($CFG->dirroot . '/user/lib.php'); require_once($CFG->dirroot . '/user/editlib.php'); require_once($CFG->dirroot . '/message/lib.php'); if (empty($userid)) { $userid = $USER->id; } $systemcontext = context_system::instance(); self::validate_context($systemcontext); $params = array( 'userid' => $userid, 'emailstop' => $emailstop, 'preferences' => $preferences ); $params = self::validate_parameters(self::update_user_preferences_parameters(), $params); $preferences = $params['preferences']; // Preferences. if (!empty($preferences)) { $userpref = ['id' => $userid]; foreach ($preferences as $preference) { $userpref['preference_' . $preference['type']] = $preference['value']; } useredit_update_user_preference($userpref); } // Check if they want to update the email. if ($emailstop !== null) { $otheruser = ($userid == $USER->id) ? $USER : core_user::get_user($userid, '*', MUST_EXIST); core_user::require_active_user($otheruser); if (core_message_can_edit_message_profile($otheruser) && $otheruser->emailstop != $emailstop) { $user = new stdClass(); $user->id = $userid; $user->emailstop = $emailstop; user_update_user($user); // Update the $USER if we should. if ($userid == $USER->id) { $USER->emailstop = $emailstop; } } } return null; } /** * Returns description of method result value * * @return null * @since Moodle 3.2 */ public static function update_user_preferences_returns() { return null; } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.2 */ public static function update_users_parameters() { $userfields = [ 'id' => new external_value(core_user::get_property_type('id'), 'ID of the user'), // General. 'username' => new external_value(core_user::get_property_type('username'), 'Username policy is defined in Moodle security config.', VALUE_OPTIONAL, '', NULL_NOT_ALLOWED), 'auth' => new external_value(core_user::get_property_type('auth'), 'Auth plugins include manual, ldap, etc', VALUE_OPTIONAL, '', NULL_NOT_ALLOWED), 'suspended' => new external_value(core_user::get_property_type('suspended'), 'Suspend user account, either false to enable user login or true to disable it', VALUE_OPTIONAL), 'password' => new external_value(core_user::get_property_type('password'), 'Plain text password consisting of any characters', VALUE_OPTIONAL, '', NULL_NOT_ALLOWED), 'firstname' => new external_value(core_user::get_property_type('firstname'), 'The first name(s) of the user', VALUE_OPTIONAL, '', NULL_NOT_ALLOWED), 'lastname' => new external_value(core_user::get_property_type('lastname'), 'The family name of the user', VALUE_OPTIONAL), 'email' => new external_value(core_user::get_property_type('email'), 'A valid and unique email address', VALUE_OPTIONAL, '', NULL_NOT_ALLOWED), 'maildisplay' => new external_value(core_user::get_property_type('maildisplay'), 'Email visibility', VALUE_OPTIONAL), 'city' => new external_value(core_user::get_property_type('city'), 'Home city of the user', VALUE_OPTIONAL), 'country' => new external_value(core_user::get_property_type('country'), 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL), 'timezone' => new external_value(core_user::get_property_type('timezone'), 'Timezone code such as Australia/Perth, or 99 for default', VALUE_OPTIONAL), 'description' => new external_value(core_user::get_property_type('description'), 'User profile description, no HTML', VALUE_OPTIONAL), // User picture. 'userpicture' => new external_value(PARAM_INT, 'The itemid where the new user picture has been uploaded to, 0 to delete', VALUE_OPTIONAL), // Additional names. 'firstnamephonetic' => new external_value(core_user::get_property_type('firstnamephonetic'), 'The first name(s) phonetically of the user', VALUE_OPTIONAL), 'lastnamephonetic' => new external_value(core_user::get_property_type('lastnamephonetic'), 'The family name phonetically of the user', VALUE_OPTIONAL), 'middlename' => new external_value(core_user::get_property_type('middlename'), 'The middle name of the user', VALUE_OPTIONAL), 'alternatename' => new external_value(core_user::get_property_type('alternatename'), 'The alternate name of the user', VALUE_OPTIONAL), // Interests. 'interests' => new external_value(PARAM_TEXT, 'User interests (separated by commas)', VALUE_OPTIONAL), // Optional. 'idnumber' => new external_value(core_user::get_property_type('idnumber'), 'An arbitrary ID code number perhaps from the institution', VALUE_OPTIONAL), 'institution' => new external_value(core_user::get_property_type('institution'), 'Institution', VALUE_OPTIONAL), 'department' => new external_value(core_user::get_property_type('department'), 'Department', VALUE_OPTIONAL), 'phone1' => new external_value(core_user::get_property_type('phone1'), 'Phone', VALUE_OPTIONAL), 'phone2' => new external_value(core_user::get_property_type('phone2'), 'Mobile phone', VALUE_OPTIONAL), 'address' => new external_value(core_user::get_property_type('address'), 'Postal address', VALUE_OPTIONAL), // Other user preferences stored in the user table. 'lang' => new external_value(core_user::get_property_type('lang'), 'Language code such as "en", must exist on server', VALUE_OPTIONAL, '', NULL_NOT_ALLOWED), 'calendartype' => new external_value(core_user::get_property_type('calendartype'), 'Calendar type such as "gregorian", must exist on server', VALUE_OPTIONAL, '', NULL_NOT_ALLOWED), 'theme' => new external_value(core_user::get_property_type('theme'), 'Theme name such as "standard", must exist on server', VALUE_OPTIONAL), 'mailformat' => new external_value(core_user::get_property_type('mailformat'), 'Mail format code is 0 for plain text, 1 for HTML etc', VALUE_OPTIONAL), // Custom user profile fields. 'customfields' => new external_multiple_structure( new external_single_structure( [ 'type' => new external_value(PARAM_ALPHANUMEXT, 'The name of the custom field'), 'value' => new external_value(PARAM_RAW, 'The value of the custom field') ] ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL), // User preferences. 'preferences' => new external_multiple_structure( new external_single_structure( [ 'type' => new external_value(PARAM_RAW, 'The name of the preference'), 'value' => new external_value(PARAM_RAW, 'The value of the preference') ] ), 'User preferences', VALUE_OPTIONAL), ]; return new external_function_parameters( [ 'users' => new external_multiple_structure( new external_single_structure($userfields) ) ] ); } /** * Update users * * @param array $users * @return null * @since Moodle 2.2 */ public static function update_users($users) { global $CFG, $DB, $USER; require_once($CFG->dirroot."/user/lib.php"); require_once($CFG->dirroot."/user/profile/lib.php"); // Required for customfields related function. require_once($CFG->dirroot.'/user/editlib.php'); // Ensure the current user is allowed to run this function. $context = context_system::instance(); require_capability('moodle/user:update', $context); self::validate_context($context); $params = self::validate_parameters(self::update_users_parameters(), array('users' => $users)); $filemanageroptions = array('maxbytes' => $CFG->maxbytes, 'subdirs' => 0, 'maxfiles' => 1, 'accepted_types' => 'optimised_image'); $warnings = array(); foreach ($params['users'] as $user) { // Catch any exception while updating a user and return it as a warning. try { $transaction = $DB->start_delegated_transaction(); // First check the user exists. if (!$existinguser = core_user::get_user($user['id'])) { throw new moodle_exception('invaliduserid', '', '', null, 'Invalid user ID'); } // Check if we are trying to update an admin. if ($existinguser->id != $USER->id and is_siteadmin($existinguser) and !is_siteadmin($USER)) { throw new moodle_exception('usernotupdatedadmin', '', '', null, 'Cannot update admin accounts'); } // Other checks (deleted, remote or guest users). if ($existinguser->deleted) { throw new moodle_exception('usernotupdateddeleted', '', '', null, 'User is a deleted user'); } if (is_mnet_remote_user($existinguser)) { throw new moodle_exception('usernotupdatedremote', '', '', null, 'User is a remote user'); } if (isguestuser($existinguser->id)) { throw new moodle_exception('usernotupdatedguest', '', '', null, 'Cannot update guest account'); } // Check duplicated emails. if (isset($user['email']) && $user['email'] !== $existinguser->email) { if (!validate_email($user['email'])) { throw new moodle_exception('useremailinvalid', '', '', null, 'Invalid email address'); } else if (empty($CFG->allowaccountssameemail)) { // Make a case-insensitive query for the given email address // and make sure to exclude the user being updated. $select = $DB->sql_equal('email', ':email', false) . ' AND mnethostid = :mnethostid AND id <> :userid'; $params = array( 'email' => $user['email'], 'mnethostid' => $CFG->mnet_localhost_id, 'userid' => $user['id'] ); // Skip if there are other user(s) that already have the same email. if ($DB->record_exists_select('user', $select, $params)) { throw new moodle_exception('useremailduplicate', '', '', null, 'Duplicate email address'); } } } user_update_user($user, true, false); $userobject = (object)$user; // Update user picture if it was specified for this user. if (empty($CFG->disableuserimages) && isset($user['userpicture'])) { $userobject->deletepicture = null; if ($user['userpicture'] == 0) { $userobject->deletepicture = true; } else { $userobject->imagefile = $user['userpicture']; } core_user::update_picture($userobject, $filemanageroptions); } // Update user interests. if (!empty($user['interests'])) { $trimmedinterests = array_map('trim', explode(',', $user['interests'])); $interests = array_filter($trimmedinterests, function($value) { return !empty($value); }); useredit_update_interests($userobject, $interests); } // Update user custom fields. if (!empty($user['customfields'])) { foreach ($user['customfields'] as $customfield) { // Profile_save_data() saves profile file it's expecting a user with the correct id, // and custom field to be named profile_field_"shortname". $user["profile_field_".$customfield['type']] = $customfield['value']; } profile_save_data((object) $user); } // Trigger event. \core\event\user_updated::create_from_userid($user['id'])->trigger(); // Preferences. if (!empty($user['preferences'])) { $userpref = clone($existinguser); foreach ($user['preferences'] as $preference) { $userpref->{'preference_'.$preference['type']} = $preference['value']; } useredit_update_user_preference($userpref); } if (isset($user['suspended']) and $user['suspended']) { \core\session\manager::destroy_user_sessions($user['id']); } $transaction->allow_commit(); } catch (Exception $e) { try { $transaction->rollback($e); } catch (Exception $e) { $warning = []; $warning['item'] = 'user'; $warning['itemid'] = $user['id']; if ($e instanceof moodle_exception) { $warning['warningcode'] = $e->errorcode; } else { $warning['warningcode'] = $e->getCode(); } $warning['message'] = $e->getMessage(); $warnings[] = $warning; } } } return ['warnings' => $warnings]; } /** * Returns description of method result value * * @return external_description * @since Moodle 2.2 */ public static function update_users_returns() { return new external_single_structure( array( 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.4 */ public static function get_users_by_field_parameters() { return new external_function_parameters( array( 'field' => new external_value(PARAM_ALPHA, 'the search field can be \'id\' or \'idnumber\' or \'username\' or \'email\''), 'values' => new external_multiple_structure( new external_value(PARAM_RAW, 'the value to match')) ) ); } /** * Get user information for a unique field. * * @throws coding_exception * @throws invalid_parameter_exception * @param string $field * @param array $values * @return array An array of arrays containg user profiles. * @since Moodle 2.4 */ public static function get_users_by_field($field, $values) { global $CFG, $USER, $DB; require_once($CFG->dirroot . "/user/lib.php"); $params = self::validate_parameters(self::get_users_by_field_parameters(), array('field' => $field, 'values' => $values)); // This array will keep all the users that are allowed to be searched, // according to the current user's privileges. $cleanedvalues = array(); switch ($field) { case 'id': $paramtype = core_user::get_property_type('id'); break; case 'idnumber': $paramtype = core_user::get_property_type('idnumber'); break; case 'username': $paramtype = core_user::get_property_type('username'); break; case 'email': $paramtype = core_user::get_property_type('email'); break; default: throw new coding_exception('invalid field parameter', 'The search field \'' . $field . '\' is not supported, look at the web service documentation'); } // Clean the values. foreach ($values as $value) { $cleanedvalue = clean_param($value, $paramtype); if ( $value != $cleanedvalue) { throw new invalid_parameter_exception('The field \'' . $field . '\' value is invalid: ' . $value . '(cleaned value: '.$cleanedvalue.')'); } $cleanedvalues[] = $cleanedvalue; } // Retrieve the users. $users = $DB->get_records_list('user', $field, $cleanedvalues, 'id'); $context = context_system::instance(); self::validate_context($context); // Finally retrieve each users information. $returnedusers = array(); foreach ($users as $user) { $userdetails = user_get_user_details_courses($user); // Return the user only if the searched field is returned. // Otherwise it means that the $USER was not allowed to search the returned user. if (!empty($userdetails) and !empty($userdetails[$field])) { $returnedusers[] = $userdetails; } } return $returnedusers; } /** * Returns description of method result value * * @return external_multiple_structure * @since Moodle 2.4 */ public static function get_users_by_field_returns() { return new external_multiple_structure(self::user_description()); } /** * Returns description of get_users() parameters. * * @return external_function_parameters * @since Moodle 2.5 */ public static function get_users_parameters() { return new external_function_parameters( array( 'criteria' => new external_multiple_structure( new external_single_structure( array( 'key' => new external_value(PARAM_ALPHA, 'the user column to search, expected keys (value format) are: "id" (int) matching user id, "lastname" (string) user last name (Note: you can use % for searching but it may be considerably slower!), "firstname" (string) user first name (Note: you can use % for searching but it may be considerably slower!), "idnumber" (string) matching user idnumber, "username" (string) matching user username, "email" (string) user email (Note: you can use % for searching but it may be considerably slower!), "auth" (string) matching user auth plugin'), 'value' => new external_value(PARAM_RAW, 'the value to search') ) ), 'the key/value pairs to be considered in user search. Values can not be empty. Specify different keys only once (fullname => \'user1\', auth => \'manual\', ...) - key occurences are forbidden. The search is executed with AND operator on the criterias. Invalid criterias (keys) are ignored, the search is still executed on the valid criterias. You can search without criteria, but the function is not designed for it. It could very slow or timeout. The function is designed to search some specific users.' ) ) ); } /** * Retrieve matching user. * * @throws moodle_exception * @param array $criteria the allowed array keys are id/lastname/firstname/idnumber/username/email/auth. * @return array An array of arrays containing user profiles. * @since Moodle 2.5 */ public static function get_users($criteria = array()) { global $CFG, $USER, $DB; require_once($CFG->dirroot . "/user/lib.php"); $params = self::validate_parameters(self::get_users_parameters(), array('criteria' => $criteria)); // Validate the criteria and retrieve the users. $users = array(); $warnings = array(); $sqlparams = array(); $usedkeys = array(); // Do not retrieve deleted users. $sql = ' deleted = 0'; foreach ($params['criteria'] as $criteriaindex => $criteria) { // Check that the criteria has never been used. if (array_key_exists($criteria['key'], $usedkeys)) { throw new moodle_exception('keyalreadyset', '', '', null, 'The key ' . $criteria['key'] . ' can only be sent once'); } else { $usedkeys[$criteria['key']] = true; } $invalidcriteria = false; // Clean the parameters. $paramtype = PARAM_RAW; switch ($criteria['key']) { case 'id': $paramtype = core_user::get_property_type('id'); break; case 'idnumber': $paramtype = core_user::get_property_type('idnumber'); break; case 'username': $paramtype = core_user::get_property_type('username'); break; case 'email': // We use PARAM_RAW to allow searches with %. $paramtype = core_user::get_property_type('email'); break; case 'auth': $paramtype = core_user::get_property_type('auth'); break; case 'lastname': case 'firstname': $paramtype = core_user::get_property_type('firstname'); break; default: // Send back a warning that this search key is not supported in this version. // This warning will make the function extandable without breaking clients. $warnings[] = array( 'item' => $criteria['key'], 'warningcode' => 'invalidfieldparameter', 'message' => 'The search key \'' . $criteria['key'] . '\' is not supported, look at the web service documentation' ); // Do not add this invalid criteria to the created SQL request. $invalidcriteria = true; unset($params['criteria'][$criteriaindex]); break; } if (!$invalidcriteria) { $cleanedvalue = clean_param($criteria['value'], $paramtype); $sql .= ' AND '; // Create the SQL. switch ($criteria['key']) { case 'id': case 'idnumber': case 'username': case 'auth': $sql .= $criteria['key'] . ' = :' . $criteria['key']; $sqlparams[$criteria['key']] = $cleanedvalue; break; case 'email': case 'lastname': case 'firstname': $sql .= $DB->sql_like($criteria['key'], ':' . $criteria['key'], false); $sqlparams[$criteria['key']] = $cleanedvalue; break; default: break; } } } $users = $DB->get_records_select('user', $sql, $sqlparams, 'id ASC'); // Finally retrieve each users information. $returnedusers = array(); foreach ($users as $user) { $userdetails = user_get_user_details_courses($user); // Return the user only if all the searched fields are returned. // Otherwise it means that the $USER was not allowed to search the returned user. if (!empty($userdetails)) { $validuser = true; foreach ($params['criteria'] as $criteria) { if (empty($userdetails[$criteria['key']])) { $validuser = false; } } if ($validuser) { $returnedusers[] = $userdetails; } } } return array('users' => $returnedusers, 'warnings' => $warnings); } /** * Returns description of get_users result value. * * @return external_description * @since Moodle 2.5 */ public static function get_users_returns() { return new external_single_structure( array('users' => new external_multiple_structure( self::user_description() ), 'warnings' => new external_warnings('always set to \'key\'', 'faulty key name') ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.2 */ public static function get_course_user_profiles_parameters() { return new external_function_parameters( array( 'userlist' => new external_multiple_structure( new external_single_structure( array( 'userid' => new external_value(core_user::get_property_type('id'), 'userid'), 'courseid' => new external_value(PARAM_INT, 'courseid'), ) ) ) ) ); } /** * Get course participant's details * * @param array $userlist array of user ids and according course ids * @return array An array of arrays describing course participants * @since Moodle 2.2 */ public static function get_course_user_profiles($userlist) { global $CFG, $USER, $DB; require_once($CFG->dirroot . "/user/lib.php"); $params = self::validate_parameters(self::get_course_user_profiles_parameters(), array('userlist' => $userlist)); $userids = array(); $courseids = array(); foreach ($params['userlist'] as $value) { $userids[] = $value['userid']; $courseids[$value['userid']] = $value['courseid']; } // Cache all courses. $courses = array(); list($sqlcourseids, $params) = $DB->get_in_or_equal(array_unique($courseids), SQL_PARAMS_NAMED); $cselect = ', ' . context_helper::get_preload_record_columns_sql('ctx'); $cjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)"; $params['contextlevel'] = CONTEXT_COURSE; $coursesql = "SELECT c.* $cselect FROM {course} c $cjoin WHERE c.id $sqlcourseids"; $rs = $DB->get_recordset_sql($coursesql, $params); foreach ($rs as $course) { // Adding course contexts to cache. context_helper::preload_from_record($course); // Cache courses. $courses[$course->id] = $course; } $rs->close(); list($sqluserids, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); $uselect = ', ' . context_helper::get_preload_record_columns_sql('ctx'); $ujoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel)"; $params['contextlevel'] = CONTEXT_USER; $usersql = "SELECT u.* $uselect FROM {user} u $ujoin WHERE u.id $sqluserids"; $users = $DB->get_recordset_sql($usersql, $params); $result = array(); foreach ($users as $user) { if (!empty($user->deleted)) { continue; } context_helper::preload_from_record($user); $course = $courses[$courseids[$user->id]]; $context = context_course::instance($courseids[$user->id], IGNORE_MISSING); self::validate_context($context); if ($userarray = user_get_user_details($user, $course)) { $result[] = $userarray; } } $users->close(); return $result; } /** * Returns description of method result value * * @return external_description * @since Moodle 2.2 */ public static function get_course_user_profiles_returns() { $additionalfields = array( 'groups' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'group id'), 'name' => new external_value(PARAM_RAW, 'group name'), 'description' => new external_value(PARAM_RAW, 'group description'), 'descriptionformat' => new external_format_value('description'), ) ), 'user groups', VALUE_OPTIONAL), 'roles' => new external_multiple_structure( new external_single_structure( array( 'roleid' => new external_value(PARAM_INT, 'role id'), 'name' => new external_value(PARAM_RAW, 'role name'), 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'role shortname'), 'sortorder' => new external_value(PARAM_INT, 'role sortorder') ) ), 'user roles', VALUE_OPTIONAL), 'enrolledcourses' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'Id of the course'), 'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'), 'shortname' => new external_value(PARAM_RAW, 'Shortname of the course') ) ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL) ); return new external_multiple_structure(self::user_description($additionalfields)); } /** * Create user return value description. * * @param array $additionalfields some additional field * @return external_description */ public static function user_description($additionalfields = array()) { $userfields = array( 'id' => new external_value(core_user::get_property_type('id'), 'ID of the user'), 'username' => new external_value(core_user::get_property_type('username'), 'The username', VALUE_OPTIONAL), 'firstname' => new external_value(core_user::get_property_type('firstname'), 'The first name(s) of the user', VALUE_OPTIONAL), 'lastname' => new external_value(core_user::get_property_type('lastname'), 'The family name of the user', VALUE_OPTIONAL), 'fullname' => new external_value(core_user::get_property_type('firstname'), 'The fullname of the user'), 'email' => new external_value(core_user::get_property_type('email'), 'An email address - allow email as root@localhost', VALUE_OPTIONAL), 'address' => new external_value(core_user::get_property_type('address'), 'Postal address', VALUE_OPTIONAL), 'phone1' => new external_value(core_user::get_property_type('phone1'), 'Phone 1', VALUE_OPTIONAL), 'phone2' => new external_value(core_user::get_property_type('phone2'), 'Phone 2', VALUE_OPTIONAL), 'department' => new external_value(core_user::get_property_type('department'), 'department', VALUE_OPTIONAL), 'institution' => new external_value(core_user::get_property_type('institution'), 'institution', VALUE_OPTIONAL), 'idnumber' => new external_value(core_user::get_property_type('idnumber'), 'An arbitrary ID code number perhaps from the institution', VALUE_OPTIONAL), 'interests' => new external_value(PARAM_TEXT, 'user interests (separated by commas)', VALUE_OPTIONAL), 'firstaccess' => new external_value(core_user::get_property_type('firstaccess'), 'first access to the site (0 if never)', VALUE_OPTIONAL), 'lastaccess' => new external_value(core_user::get_property_type('lastaccess'), 'last access to the site (0 if never)', VALUE_OPTIONAL), 'auth' => new external_value(core_user::get_property_type('auth'), 'Auth plugins include manual, ldap, etc', VALUE_OPTIONAL), 'suspended' => new external_value(core_user::get_property_type('suspended'), 'Suspend user account, either false to enable user login or true to disable it', VALUE_OPTIONAL), 'confirmed' => new external_value(core_user::get_property_type('confirmed'), 'Active user: 1 if confirmed, 0 otherwise', VALUE_OPTIONAL), 'lang' => new external_value(core_user::get_property_type('lang'), 'Language code such as "en", must exist on server', VALUE_OPTIONAL), 'calendartype' => new external_value(core_user::get_property_type('calendartype'), 'Calendar type such as "gregorian", must exist on server', VALUE_OPTIONAL), 'theme' => new external_value(core_user::get_property_type('theme'), 'Theme name such as "standard", must exist on server', VALUE_OPTIONAL), 'timezone' => new external_value(core_user::get_property_type('timezone'), 'Timezone code such as Australia/Perth, or 99 for default', VALUE_OPTIONAL), 'mailformat' => new external_value(core_user::get_property_type('mailformat'), 'Mail format code is 0 for plain text, 1 for HTML etc', VALUE_OPTIONAL), 'trackforums' => new external_value(core_user::get_property_type('trackforums'), 'Whether the user is tracking forums.', VALUE_OPTIONAL), 'description' => new external_value(core_user::get_property_type('description'), 'User profile description', VALUE_OPTIONAL), 'descriptionformat' => new external_format_value(core_user::get_property_type('descriptionformat'), VALUE_OPTIONAL), 'city' => new external_value(core_user::get_property_type('city'), 'Home city of the user', VALUE_OPTIONAL), 'country' => new external_value(core_user::get_property_type('country'), 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL), 'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small version'), 'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version'), 'customfields' => new external_multiple_structure( new external_single_structure( array( 'type' => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field - text field, checkbox...'), 'value' => new external_value(PARAM_RAW, 'The value of the custom field (as stored in the database)'), 'displayvalue' => new external_value(PARAM_RAW, 'The value of the custom field for display', VALUE_OPTIONAL), 'name' => new external_value(PARAM_RAW, 'The name of the custom field'), 'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field - to be able to build the field class in the code'), ) ), 'User custom fields (also known as user profile fields)', VALUE_OPTIONAL), 'preferences' => new external_multiple_structure( new external_single_structure( array( 'name' => new external_value(PARAM_RAW, 'The name of the preferences'), 'value' => new external_value(PARAM_RAW, 'The value of the preference'), ) ), 'Users preferences', VALUE_OPTIONAL) ); if (!empty($additionalfields)) { $userfields = array_merge($userfields, $additionalfields); } return new external_single_structure($userfields); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.6 */ public static function add_user_private_files_parameters() { return new external_function_parameters( array( 'draftid' => new external_value(PARAM_INT, 'draft area id') ) ); } /** * Copy files from a draft area to users private files area. * * @throws invalid_parameter_exception * @throws moodle_exception * @param int $draftid Id of a draft area containing files. * @return array An array of warnings * @since Moodle 2.6 */ public static function add_user_private_files($draftid) { global $CFG, $USER; require_once($CFG->libdir . "/filelib.php"); $params = self::validate_parameters(self::add_user_private_files_parameters(), array('draftid' => $draftid)); if (isguestuser()) { throw new invalid_parameter_exception('Guest users cannot upload files'); } $context = context_user::instance($USER->id); require_capability('moodle/user:manageownfiles', $context); $maxbytes = $CFG->userquota; $maxareabytes = $CFG->userquota; if (has_capability('moodle/user:ignoreuserquota', $context)) { $maxbytes = USER_CAN_IGNORE_FILE_SIZE_LIMITS; $maxareabytes = FILE_AREA_MAX_BYTES_UNLIMITED; } else { // Get current used space for this user (private files only). $fileareainfo = file_get_file_area_info($context->id, 'user', 'private'); $usedspace = $fileareainfo['filesize_without_references']; // Get the total size of the new files we want to add to private files. $newfilesinfo = file_get_draft_area_info($params['draftid']); if (($newfilesinfo['filesize_without_references'] + $usedspace) > $maxareabytes) { throw new moodle_exception('maxareabytes'); } } $options = array('subdirs' => 1, 'maxbytes' => $maxbytes, 'maxfiles' => -1, 'areamaxbytes' => $maxareabytes); file_merge_files_from_draft_area_into_filearea($draftid, $context->id, 'user', 'private', 0, $options); return null; } /** * Returns description of method result value * * @return external_description * @since Moodle 2.2 */ public static function add_user_private_files_returns() { return null; } /** * Returns description of method parameters. * * @return external_function_parameters * @since Moodle 2.6 */ public static function add_user_device_parameters() { return new external_function_parameters( array( 'appid' => new external_value(PARAM_NOTAGS, 'the app id, usually something like com.moodle.moodlemobile'), 'name' => new external_value(PARAM_NOTAGS, 'the device name, \'occam\' or \'iPhone\' etc.'), 'model' => new external_value(PARAM_NOTAGS, 'the device model \'Nexus4\' or \'iPad1,1\' etc.'), 'platform' => new external_value(PARAM_NOTAGS, 'the device platform \'iOS\' or \'Android\' etc.'), 'version' => new external_value(PARAM_NOTAGS, 'the device version \'6.1.2\' or \'4.2.2\' etc.'), 'pushid' => new external_value(PARAM_RAW, 'the device PUSH token/key/identifier/registration id'), 'uuid' => new external_value(PARAM_RAW, 'the device UUID'), 'publickey' => new external_value(PARAM_RAW, 'the app generated public key', VALUE_DEFAULT, null), ) ); } /** * Add a user device in Moodle database (for PUSH notifications usually). * * @throws moodle_exception * @param string $appid The app id, usually something like com.moodle.moodlemobile. * @param string $name The device name, occam or iPhone etc. * @param string $model The device model Nexus4 or iPad1.1 etc. * @param string $platform The device platform iOs or Android etc. * @param string $version The device version 6.1.2 or 4.2.2 etc. * @param string $pushid The device PUSH token/key/identifier/registration id. * @param string $uuid The device UUID. * @param string $publickey The app generated public key * @return array List of possible warnings. * @since Moodle 2.6 */ public static function add_user_device($appid, $name, $model, $platform, $version, $pushid, $uuid, $publickey = null) { global $CFG, $USER, $DB; require_once($CFG->dirroot . "/user/lib.php"); $params = self::validate_parameters(self::add_user_device_parameters(), array('appid' => $appid, 'name' => $name, 'model' => $model, 'platform' => $platform, 'version' => $version, 'pushid' => $pushid, 'uuid' => $uuid, 'publickey' => $publickey, )); $warnings = array(); // Prevent duplicate keys for users. if ($DB->get_record('user_devices', array('pushid' => $params['pushid'], 'userid' => $USER->id))) { $warnings['warning'][] = array( 'item' => $params['pushid'], 'warningcode' => 'existingkeyforthisuser', 'message' => 'This key is already stored for this user' ); return $warnings; } // Notice that we can have multiple devices because previously it was allowed to have repeated ones. // Since we don't have a clear way to decide which one is the more appropiate, we update all. if ($userdevices = $DB->get_records('user_devices', array('uuid' => $params['uuid'], 'appid' => $params['appid'], 'userid' => $USER->id))) { foreach ($userdevices as $userdevice) { $userdevice->version = $params['version']; // Maybe the user upgraded the device. $userdevice->pushid = $params['pushid']; $userdevice->publickey = $params['publickey']; $userdevice->timemodified = time(); $DB->update_record('user_devices', $userdevice); } } else { $userdevice = new stdclass; $userdevice->userid = $USER->id; $userdevice->appid = $params['appid']; $userdevice->name = $params['name']; $userdevice->model = $params['model']; $userdevice->platform = $params['platform']; $userdevice->version = $params['version']; $userdevice->pushid = $params['pushid']; $userdevice->uuid = $params['uuid']; $userdevice->publickey = $params['publickey']; $userdevice->timecreated = time(); $userdevice->timemodified = $userdevice->timecreated; if (!$DB->insert_record('user_devices', $userdevice)) { throw new moodle_exception("There was a problem saving in the database the device with key: " . $params['pushid']); } } return $warnings; } /** * Returns description of method result value. * * @return external_multiple_structure * @since Moodle 2.6 */ public static function add_user_device_returns() { return new external_multiple_structure( new external_warnings() ); } /** * Returns description of method parameters. * * @return external_function_parameters * @since Moodle 2.9 */ public static function remove_user_device_parameters() { return new external_function_parameters( array( 'uuid' => new external_value(PARAM_RAW, 'the device UUID'), 'appid' => new external_value(PARAM_NOTAGS, 'the app id, if empty devices matching the UUID for the user will be removed', VALUE_DEFAULT, ''), ) ); } /** * Remove a user device from the Moodle database (for PUSH notifications usually). * * @param string $uuid The device UUID. * @param string $appid The app id, opitonal parameter. If empty all the devices fmatching the UUID or the user will be removed. * @return array List of possible warnings and removal status. * @since Moodle 2.9 */ public static function remove_user_device($uuid, $appid = "") { global $CFG; require_once($CFG->dirroot . "/user/lib.php"); $params = self::validate_parameters(self::remove_user_device_parameters(), array('uuid' => $uuid, 'appid' => $appid)); $context = context_system::instance(); self::validate_context($context); // Warnings array, it can be empty at the end but is mandatory. $warnings = array(); $removed = user_remove_user_device($params['uuid'], $params['appid']); if (!$removed) { $warnings[] = array( 'item' => $params['uuid'], 'warningcode' => 'devicedoesnotexist', 'message' => 'The device doesn\'t exists in the database' ); } $result = array( 'removed' => $removed, 'warnings' => $warnings ); return $result; } /** * Returns description of method result value. * * @return external_multiple_structure * @since Moodle 2.9 */ public static function remove_user_device_returns() { return new external_single_structure( array( 'removed' => new external_value(PARAM_BOOL, 'True if removed, false if not removed because it doesn\'t exists'), 'warnings' => new external_warnings(), ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.9 */ public static function view_user_list_parameters() { return new external_function_parameters( array( 'courseid' => new external_value(PARAM_INT, 'id of the course, 0 for site') ) ); } /** * Trigger the user_list_viewed event. * * @param int $courseid id of course * @return array of warnings and status result * @since Moodle 2.9 * @throws moodle_exception */ public static function view_user_list($courseid) { global $CFG; require_once($CFG->dirroot . "/user/lib.php"); require_once($CFG->dirroot . '/course/lib.php'); $params = self::validate_parameters(self::view_user_list_parameters(), array( 'courseid' => $courseid )); $warnings = array(); if (empty($params['courseid'])) { $params['courseid'] = SITEID; } $course = get_course($params['courseid']); if ($course->id == SITEID) { $context = context_system::instance(); } else { $context = context_course::instance($course->id); } self::validate_context($context); course_require_view_participants($context); user_list_view($course, $context); $result = array(); $result['status'] = true; $result['warnings'] = $warnings; return $result; } /** * Returns description of method result value * * @return external_description * @since Moodle 2.9 */ public static function view_user_list_returns() { return new external_single_structure( array( 'status' => new external_value(PARAM_BOOL, 'status: true if success'), 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.9 */ public static function view_user_profile_parameters() { return new external_function_parameters( array( 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_REQUIRED), 'courseid' => new external_value(PARAM_INT, 'id of the course, default site course', VALUE_DEFAULT, 0) ) ); } /** * Trigger the user profile viewed event. * * @param int $userid id of user * @param int $courseid id of course * @return array of warnings and status result * @since Moodle 2.9 * @throws moodle_exception */ public static function view_user_profile($userid, $courseid = 0) { global $CFG, $USER; require_once($CFG->dirroot . "/user/profile/lib.php"); $params = self::validate_parameters(self::view_user_profile_parameters(), array( 'userid' => $userid, 'courseid' => $courseid )); $warnings = array(); if (empty($params['userid'])) { $params['userid'] = $USER->id; } if (empty($params['courseid'])) { $params['courseid'] = SITEID; } $course = get_course($params['courseid']); $user = core_user::get_user($params['userid'], '*', MUST_EXIST); core_user::require_active_user($user); if ($course->id == SITEID) { $coursecontext = context_system::instance();; } else { $coursecontext = context_course::instance($course->id); } self::validate_context($coursecontext); $currentuser = $USER->id == $user->id; $usercontext = context_user::instance($user->id); if (!$currentuser and !has_capability('moodle/user:viewdetails', $coursecontext) and !has_capability('moodle/user:viewdetails', $usercontext)) { throw new moodle_exception('cannotviewprofile'); } // Case like user/profile.php. if ($course->id == SITEID) { profile_view($user, $usercontext); } else { // Case like user/view.php. if (!$currentuser and !can_access_course($course, $user, '', true)) { throw new moodle_exception('notenrolledprofile'); } profile_view($user, $coursecontext, $course); } $result = array(); $result['status'] = true; $result['warnings'] = $warnings; return $result; } /** * Returns description of method result value * * @return external_description * @since Moodle 2.9 */ public static function view_user_profile_returns() { return new external_single_structure( array( 'status' => new external_value(PARAM_BOOL, 'status: true if success'), 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.2 */ public static function get_user_preferences_parameters() { return new external_function_parameters( array( 'name' => new external_value(PARAM_RAW, 'preference name, empty for all', VALUE_DEFAULT, ''), 'userid' => new external_value(PARAM_INT, 'id of the user, default to current user', VALUE_DEFAULT, 0) ) ); } /** * Return user preferences. * * @param string $name preference name, empty for all * @param int $userid id of the user, 0 for current user * @return array of warnings and preferences * @since Moodle 3.2 * @throws moodle_exception */ public static function get_user_preferences($name = '', $userid = 0) { global $USER; $params = self::validate_parameters(self::get_user_preferences_parameters(), array( 'name' => $name, 'userid' => $userid )); $preferences = array(); $warnings = array(); $context = context_system::instance(); self::validate_context($context); if (empty($params['name'])) { $name = null; } if (empty($params['userid'])) { $user = null; } else { $user = core_user::get_user($params['userid'], '*', MUST_EXIST); core_user::require_active_user($user); if ($user->id != $USER->id) { // Only admins can retrieve other users preferences. require_capability('moodle/site:config', $context); } } $userpreferences = get_user_preferences($name, null, $user); // Check if we received just one preference. if (!is_array($userpreferences)) { $userpreferences = array($name => $userpreferences); } foreach ($userpreferences as $name => $value) { $preferences[] = array( 'name' => $name, 'value' => $value, ); } $result = array(); $result['preferences'] = $preferences; $result['warnings'] = $warnings; return $result; } /** * Returns description of method result value * * @return external_description * @since Moodle 3.2 */ public static function get_user_preferences_returns() { return new external_single_structure( array( 'preferences' => new external_multiple_structure( new external_single_structure( array( 'name' => new external_value(PARAM_RAW, 'The name of the preference'), 'value' => new external_value(PARAM_RAW, 'The value of the preference'), ) ), 'User custom fields (also known as user profile fields)' ), 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.2 */ public static function update_picture_parameters() { return new external_function_parameters( array( 'draftitemid' => new external_value(PARAM_INT, 'Id of the user draft file to use as image'), 'delete' => new external_value(PARAM_BOOL, 'If we should delete the user picture', VALUE_DEFAULT, false), 'userid' => new external_value(PARAM_INT, 'Id of the user, 0 for current user', VALUE_DEFAULT, 0) ) ); } /** * Update or delete the user picture in the site * * @param int $draftitemid id of the user draft file to use as image * @param bool $delete if we should delete the user picture * @param int $userid id of the user, 0 for current user * @return array warnings and success status * @since Moodle 3.2 * @throws moodle_exception */ public static function update_picture($draftitemid, $delete = false, $userid = 0) { global $CFG, $USER, $PAGE; $params = self::validate_parameters( self::update_picture_parameters(), array( 'draftitemid' => $draftitemid, 'delete' => $delete, 'userid' => $userid ) ); $context = context_system::instance(); self::validate_context($context); if (!empty($CFG->disableuserimages)) { throw new moodle_exception('userimagesdisabled', 'admin'); } if (empty($params['userid']) or $params['userid'] == $USER->id) { $user = $USER; require_capability('moodle/user:editownprofile', $context); } else { $user = core_user::get_user($params['userid'], '*', MUST_EXIST); core_user::require_active_user($user); $personalcontext = context_user::instance($user->id); require_capability('moodle/user:editprofile', $personalcontext); if (is_siteadmin($user) and !is_siteadmin($USER)) { // Only admins may edit other admins. throw new moodle_exception('useradmineditadmin'); } } // Load the appropriate auth plugin. $userauth = get_auth_plugin($user->auth); if (is_mnet_remote_user($user) or !$userauth->can_edit_profile() or $userauth->edit_profile_url()) { throw new moodle_exception('noprofileedit', 'auth'); } $filemanageroptions = array( 'maxbytes' => $CFG->maxbytes, 'subdirs' => 0, 'maxfiles' => 1, 'accepted_types' => 'optimised_image' ); $user->deletepicture = $params['delete']; $user->imagefile = $params['draftitemid']; $success = core_user::update_picture($user, $filemanageroptions); $result = array( 'success' => $success, 'warnings' => array(), ); if ($success) { $userpicture = new user_picture(core_user::get_user($user->id)); $userpicture->size = 1; // Size f1. $result['profileimageurl'] = $userpicture->get_url($PAGE)->out(false); } return $result; } /** * Returns description of method result value * * @return external_description * @since Moodle 3.2 */ public static function update_picture_returns() { return new external_single_structure( array( 'success' => new external_value(PARAM_BOOL, 'True if the image was updated, false otherwise.'), 'profileimageurl' => new external_value(PARAM_URL, 'New profile user image url', VALUE_OPTIONAL), 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.2 */ public static function set_user_preferences_parameters() { return new external_function_parameters( array( 'preferences' => new external_multiple_structure( new external_single_structure( array( 'name' => new external_value(PARAM_RAW, 'The name of the preference'), 'value' => new external_value(PARAM_RAW, 'The value of the preference'), 'userid' => new external_value(PARAM_INT, 'Id of the user to set the preference (default to current user)', VALUE_DEFAULT, 0), ) ) ) ) ); } /** * Set user preferences. * * @param array $preferences list of preferences including name, value and userid * @return array of warnings and preferences saved * @since Moodle 3.2 * @throws moodle_exception */ public static function set_user_preferences($preferences) { global $PAGE, $USER; $params = self::validate_parameters(self::set_user_preferences_parameters(), array('preferences' => $preferences)); $warnings = array(); $saved = array(); $context = context_system::instance(); $PAGE->set_context($context); $userscache = array(); foreach ($params['preferences'] as $pref) { $userid = $pref['userid'] ?: $USER->id; // Check to which user set the preference. if (!empty($userscache[$userid])) { $user = $userscache[$userid]; } else { try { $user = core_user::get_user($userid, '*', MUST_EXIST); core_user::require_active_user($user); $userscache[$userid] = $user; } catch (Exception $e) { $warnings[] = array( 'item' => 'user', 'itemid' => $userid, 'warningcode' => 'invaliduser', 'message' => $e->getMessage() ); continue; } } try { if (core_user::can_edit_preference($pref['name'], $user)) { $value = core_user::clean_preference($pref['value'], $pref['name']); set_user_preference($pref['name'], $value, $user->id); $saved[] = array( 'name' => $pref['name'], 'userid' => $user->id, ); } else { $warnings[] = array( 'item' => 'user', 'itemid' => $user->id, 'warningcode' => 'nopermission', 'message' => 'You are not allowed to change the preference '.s($pref['name']).' for user '.$user->id ); } } catch (Exception $e) { $warnings[] = array( 'item' => 'user', 'itemid' => $user->id, 'warningcode' => 'errorsavingpreference', 'message' => $e->getMessage() ); } } $result = array(); $result['saved'] = $saved; $result['warnings'] = $warnings; return $result; } /** * Returns description of method result value * * @return external_description * @since Moodle 3.2 */ public static function set_user_preferences_returns() { return new external_single_structure( array( 'saved' => new external_multiple_structure( new external_single_structure( array( 'name' => new external_value(PARAM_RAW, 'The name of the preference'), 'userid' => new external_value(PARAM_INT, 'The user the preference was set for'), ) ), 'Preferences saved' ), 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters. * * @return external_function_parameters * @since Moodle 3.2 */ public static function agree_site_policy_parameters() { return new external_function_parameters(array()); } /** * Agree the site policy for the current user. * * @return array of warnings and status result * @since Moodle 3.2 * @throws moodle_exception */ public static function agree_site_policy() { global $CFG, $DB, $USER; $warnings = array(); $context = context_system::instance(); try { // We expect an exception here since the user didn't agree the site policy yet. self::validate_context($context); } catch (Exception $e) { // We are expecting only a sitepolicynotagreed exception. if (!($e instanceof moodle_exception) or $e->errorcode != 'sitepolicynotagreed') { // In case we receive a different exception, throw it. throw $e; } } $manager = new \core_privacy\local\sitepolicy\manager(); if (!empty($USER->policyagreed)) { $status = false; $warnings[] = array( 'item' => 'user', 'itemid' => $USER->id, 'warningcode' => 'alreadyagreed', 'message' => 'The user already agreed the site policy.' ); } else if (!$manager->is_defined()) { $status = false; $warnings[] = array( 'item' => 'user', 'itemid' => $USER->id, 'warningcode' => 'nositepolicy', 'message' => 'The site does not have a site policy configured.' ); } else { $status = $manager->accept(); } $result = array(); $result['status'] = $status; $result['warnings'] = $warnings; return $result; } /** * Returns description of method result value. * * @return external_description * @since Moodle 3.2 */ public static function agree_site_policy_returns() { return new external_single_structure( array( 'status' => new external_value(PARAM_BOOL, 'Status: true only if we set the policyagreed to 1 for the user'), 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters. * * @return external_function_parameters * @since Moodle 3.4 */ public static function get_private_files_info_parameters() { return new external_function_parameters( array( 'userid' => new external_value(PARAM_INT, 'Id of the user, default to current user.', VALUE_DEFAULT, 0) ) ); } /** * Returns general information about files in the user private files area. * * @param int $userid Id of the user, default to current user. * @return array of warnings and file area information * @since Moodle 3.4 * @throws moodle_exception */ public static function get_private_files_info($userid = 0) { global $CFG, $USER; require_once($CFG->libdir . '/filelib.php'); $params = self::validate_parameters(self::get_private_files_info_parameters(), array('userid' => $userid)); $warnings = array(); $context = context_system::instance(); self::validate_context($context); if (empty($params['userid']) || $params['userid'] == $USER->id) { $usercontext = context_user::instance($USER->id); require_capability('moodle/user:manageownfiles', $usercontext); } else { $user = core_user::get_user($params['userid'], '*', MUST_EXIST); core_user::require_active_user($user); // Only admins can retrieve other users information. require_capability('moodle/site:config', $context); $usercontext = context_user::instance($user->id); } $fileareainfo = file_get_file_area_info($usercontext->id, 'user', 'private'); $result = array(); $result['filecount'] = $fileareainfo['filecount']; $result['foldercount'] = $fileareainfo['foldercount']; $result['filesize'] = $fileareainfo['filesize']; $result['filesizewithoutreferences'] = $fileareainfo['filesize_without_references']; $result['warnings'] = $warnings; return $result; } /** * Returns description of method result value. * * @return external_description * @since Moodle 3.4 */ public static function get_private_files_info_returns() { return new external_single_structure( array( 'filecount' => new external_value(PARAM_INT, 'Number of files in the area.'), 'foldercount' => new external_value(PARAM_INT, 'Number of folders in the area.'), 'filesize' => new external_value(PARAM_INT, 'Total size of the files in the area.'), 'filesizewithoutreferences' => new external_value(PARAM_INT, 'Total size of the area excluding file references'), 'warnings' => new external_warnings() ) ); } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/course/externallib.php000064400000653742152266574300023236 0ustar00. /** * External course API * * @package core_course * @category external * @copyright 2009 Petr Skodak * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die; use core_course\external\course_summary_exporter; use core_external\external_api; use core_external\external_description; use core_external\external_files; use core_external\external_format_value; use core_external\external_function_parameters; use core_external\external_multiple_structure; use core_external\external_single_structure; use core_external\external_value; use core_external\external_warnings; use core_external\util; require_once(__DIR__ . "/lib.php"); /** * Course external functions * * @package core_course * @category external * @copyright 2011 Jerome Mouneyrac * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.2 */ class core_course_external extends external_api { /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.9 Options available * @since Moodle 2.2 */ public static function get_course_contents_parameters() { return new external_function_parameters( array('courseid' => new external_value(PARAM_INT, 'course id'), 'options' => new external_multiple_structure ( new external_single_structure( array( 'name' => new external_value(PARAM_ALPHANUM, 'The expected keys (value format) are: excludemodules (bool) Do not return modules, return only the sections structure excludecontents (bool) Do not return module contents (i.e: files inside a resource) includestealthmodules (bool) Return stealth modules for students in a special section (with id -1) sectionid (int) Return only this section sectionnumber (int) Return only this section with number (order) cmid (int) Return only this module information (among the whole sections structure) modname (string) Return only modules with this name "label, forum, etc..." modid (int) Return only the module with this id (to be used with modname'), 'value' => new external_value(PARAM_RAW, 'the value of the option, this param is personaly validated in the external function.') ) ), 'Options, used since Moodle 2.9', VALUE_DEFAULT, array()) ) ); } /** * Get course contents * * @param int $courseid course id * @param array $options Options for filtering the results, used since Moodle 2.9 * @return array * @since Moodle 2.9 Options available * @since Moodle 2.2 */ public static function get_course_contents($courseid, $options = array()) { global $CFG, $DB, $USER, $PAGE; require_once($CFG->dirroot . "/course/lib.php"); require_once($CFG->libdir . '/completionlib.php'); //validate parameter $params = self::validate_parameters(self::get_course_contents_parameters(), array('courseid' => $courseid, 'options' => $options)); $filters = array(); if (!empty($params['options'])) { foreach ($params['options'] as $option) { $name = trim($option['name']); // Avoid duplicated options. if (!isset($filters[$name])) { switch ($name) { case 'excludemodules': case 'excludecontents': case 'includestealthmodules': $value = clean_param($option['value'], PARAM_BOOL); $filters[$name] = $value; break; case 'sectionid': case 'sectionnumber': case 'cmid': case 'modid': $value = clean_param($option['value'], PARAM_INT); if (is_numeric($value)) { $filters[$name] = $value; } else { throw new moodle_exception('errorinvalidparam', 'webservice', '', $name); } break; case 'modname': $value = clean_param($option['value'], PARAM_PLUGIN); if ($value) { $filters[$name] = $value; } else { throw new moodle_exception('errorinvalidparam', 'webservice', '', $name); } break; default: throw new moodle_exception('errorinvalidparam', 'webservice', '', $name); } } } } //retrieve the course $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST); // now security checks $context = context_course::instance($course->id, IGNORE_MISSING); try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $course->id; throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam); } $canupdatecourse = has_capability('moodle/course:update', $context); //create return value $coursecontents = array(); if ($canupdatecourse or $course->visible or has_capability('moodle/course:viewhiddencourses', $context)) { //retrieve sections $modinfo = get_fast_modinfo($course); $sections = $modinfo->get_section_info_all(); $courseformat = course_get_format($course); $coursenumsections = $courseformat->get_last_section_number(); $stealthmodules = array(); // Array to keep all the modules available but not visible in a course section/topic. $completioninfo = new completion_info($course); //for each sections (first displayed to last displayed) $modinfosections = $modinfo->get_sections(); foreach ($sections as $key => $section) { // This becomes true when we are filtering and we found the value to filter with. $sectionfound = false; // Filter by section id. if (!empty($filters['sectionid'])) { if ($section->id != $filters['sectionid']) { continue; } else { $sectionfound = true; } } // Filter by section number. Note that 0 is a valid section number. if (isset($filters['sectionnumber'])) { if ($key != $filters['sectionnumber']) { continue; } else { $sectionfound = true; } } // reset $sectioncontents $sectionvalues = array(); $sectionvalues['id'] = $section->id; $sectionvalues['name'] = get_section_name($course, $section); // Temporary hack to be able to hide the subsections in certain app versions. if (!empty($section->component) && \core_useragent::is_moodle_app()) { $sectionvalues['name'] = html_writer::span($sectionvalues['name'], 'course-' . $section->component); } $sectionvalues['visible'] = $section->visible; $options = (object) array('noclean' => true); list($sectionvalues['summary'], $sectionvalues['summaryformat']) = \core_external\util::format_text($section->summary, $section->summaryformat, $context, 'course', 'section', $section->id, $options); $sectionvalues['section'] = $section->section; $sectionvalues['hiddenbynumsections'] = $section->section > $coursenumsections ? 1 : 0; $sectionvalues['uservisible'] = $section->uservisible; if (!empty($section->availableinfo)) { $sectionvalues['availabilityinfo'] = \core_availability\info::format_info($section->availableinfo, $course); } $sectionvalues['component'] = $section->component; $sectionvalues['itemid'] = $section->itemid; $sectioncontents = array(); // For each module of the section. if (empty($filters['excludemodules']) and !empty($modinfosections[$section->section])) { foreach ($modinfosections[$section->section] as $cmid) { $cm = $modinfo->cms[$cmid]; $cminfo = cm_info::create($cm); $activitydates = \core\activity_dates::get_dates_for_module($cminfo, $USER->id); // Stop here if the module is not visible to the user on the course main page: // The user can't access the module and the user can't view the module on the course page. if (!$cm->uservisible && !$cm->is_visible_on_course_page()) { continue; } // This becomes true when we are filtering and we found the value to filter with. $modfound = false; // Filter by cmid. if (!empty($filters['cmid'])) { if ($cmid != $filters['cmid']) { continue; } else { $modfound = true; } } // Filter by module name and id. if (!empty($filters['modname'])) { if ($cm->modname != $filters['modname']) { continue; } else if (!empty($filters['modid'])) { if ($cm->instance != $filters['modid']) { continue; } else { // Note that if we are only filtering by modname we don't break the loop. $modfound = true; } } } $module = array(); $modcontext = context_module::instance($cm->id); $isbranded = component_callback('mod_' . $cm->modname, 'is_branded', [], false); // Common info (for people being able to see the module or availability dates). $module['id'] = $cm->id; $module['name'] = \core_external\util::format_string($cm->name, $modcontext); $module['instance'] = $cm->instance; $module['contextid'] = $modcontext->id; $module['modname'] = (string) $cm->modname; $module['modplural'] = (string) $cm->modplural; $module['modicon'] = $cm->get_icon_url()->out(false); $module['purpose'] = plugin_supports('mod', $cm->modname, FEATURE_MOD_PURPOSE, MOD_PURPOSE_OTHER); $module['branded'] = $isbranded; $module['indent'] = $cm->indent; $module['onclick'] = $cm->onclick; $module['afterlink'] = $cm->afterlink; $activitybadgedata = $cm->get_activitybadge(); if (!empty($activitybadgedata)) { $module['activitybadge'] = $activitybadgedata; } $module['customdata'] = json_encode($cm->customdata); $module['completion'] = $cm->completion; $module['downloadcontent'] = $cm->downloadcontent; $module['noviewlink'] = plugin_supports('mod', $cm->modname, FEATURE_NO_VIEW_LINK, false); $module['candisplay'] = plugin_supports('mod', $cm->modname, FEATURE_CAN_DISPLAY, true); $module['dates'] = $activitydates; $module['groupmode'] = $cm->groupmode; // Check module completion. $completion = $completioninfo->is_enabled($cm); if ($completion != COMPLETION_DISABLED) { $exporter = new \core_completion\external\completion_info_exporter($course, $cm, $USER->id); $renderer = $PAGE->get_renderer('core'); $modulecompletiondata = (array)$exporter->export($renderer); $module['completiondata'] = $modulecompletiondata; } if (!empty($cm->showdescription) or $module['noviewlink']) { // We want to use the external format. However from reading get_formatted_content(), $cm->content format is always FORMAT_HTML. $options = array('noclean' => true); list($module['description'], $descriptionformat) = \core_external\util::format_text($cm->content, FORMAT_HTML, $modcontext, $cm->modname, 'intro', $cm->id, $options); } //url of the module $url = $cm->url; if ($url) { //labels don't have url $module['url'] = $url->out(false); } $canviewhidden = has_capability('moodle/course:viewhiddenactivities', context_module::instance($cm->id)); //user that can view hidden module should know about the visibility $module['visible'] = $cm->visible; $module['visibleoncoursepage'] = $cm->visibleoncoursepage; $module['uservisible'] = $cm->uservisible; if (!empty($cm->availableinfo)) { $module['availabilityinfo'] = \core_availability\info::format_info($cm->availableinfo, $course); } // Availability date (also send to user who can see hidden module). if ($CFG->enableavailability && ($canviewhidden || $canupdatecourse)) { $module['availability'] = $cm->availability; } // Return contents only if the user can access to the module. if ($cm->uservisible) { $baseurl = 'webservice/pluginfile.php'; // Call $modulename_export_contents (each module callback take care about checking the capabilities). require_once($CFG->dirroot . '/mod/' . $cm->modname . '/lib.php'); $getcontentfunction = $cm->modname.'_export_contents'; if (function_exists($getcontentfunction)) { $contents = $getcontentfunction($cm, $baseurl); $module['contentsinfo'] = array( 'filescount' => count($contents), 'filessize' => 0, 'lastmodified' => 0, 'mimetypes' => array(), ); foreach ($contents as $content) { // Check repository file (only main file). if (!isset($module['contentsinfo']['repositorytype'])) { $module['contentsinfo']['repositorytype'] = isset($content['repositorytype']) ? $content['repositorytype'] : ''; } if (isset($content['filesize'])) { $module['contentsinfo']['filessize'] += $content['filesize']; } if (isset($content['timemodified']) && ($content['timemodified'] > $module['contentsinfo']['lastmodified'])) { $module['contentsinfo']['lastmodified'] = $content['timemodified']; } if (isset($content['mimetype'])) { $module['contentsinfo']['mimetypes'][$content['mimetype']] = $content['mimetype']; } } if (empty($filters['excludecontents']) and !empty($contents)) { $module['contents'] = $contents; } else { $module['contents'] = array(); } } } // Assign result to $sectioncontents, there is an exception, // stealth activities in non-visible sections for students go to a special section. if (!empty($filters['includestealthmodules']) && !$section->uservisible && $cm->is_stealth()) { $stealthmodules[] = $module; } else { $sectioncontents[] = $module; } // If we just did a filtering, break the loop. if ($modfound) { break; } } } $sectionvalues['modules'] = $sectioncontents; // assign result to $coursecontents $coursecontents[$key] = $sectionvalues; // Break the loop if we are filtering. if ($sectionfound) { break; } } // Now that we have iterated over all the sections and activities, check the visibility. // We didn't this before to be able to retrieve stealth activities. foreach ($coursecontents as $sectionnumber => $sectioncontents) { $section = $sections[$sectionnumber]; if (!$courseformat->is_section_visible($section)) { unset($coursecontents[$sectionnumber]); continue; } // Remove section and modules information if the section is not visible for the user. if (!$section->uservisible) { $coursecontents[$sectionnumber]['modules'] = array(); // Remove summary information if the section is completely hidden only, // even if the section is not user visible, the summary is always displayed among the availability information. if (!$section->visible) { $coursecontents[$sectionnumber]['summary'] = ''; } } } // Include stealth modules in special section (without any info). if (!empty($stealthmodules)) { $coursecontents[] = array( 'id' => -1, 'name' => '', 'summary' => '', 'summaryformat' => FORMAT_MOODLE, 'modules' => $stealthmodules ); } } return $coursecontents; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.2 */ public static function get_course_contents_returns() { $completiondefinition = \core_completion\external\completion_info_exporter::get_read_structure(VALUE_DEFAULT, []); return new external_multiple_structure( new external_single_structure( [ 'id' => new external_value(PARAM_INT, 'Section ID'), 'name' => new external_value(PARAM_RAW, 'Section name'), 'visible' => new external_value(PARAM_INT, 'is the section visible', VALUE_OPTIONAL), 'summary' => new external_value(PARAM_RAW, 'Section description'), 'summaryformat' => new external_format_value('summary'), 'section' => new external_value(PARAM_INT, 'Section number inside the course', VALUE_OPTIONAL), 'hiddenbynumsections' => new external_value( PARAM_INT, 'Whether is a section hidden in the course format', VALUE_OPTIONAL, ), 'uservisible' => new external_value(PARAM_BOOL, 'Is the section visible for the user?', VALUE_OPTIONAL), 'availabilityinfo' => new external_value(PARAM_RAW, 'Availability information.', VALUE_OPTIONAL), 'component' => new external_value( PARAM_COMPONENT, 'The delegate component of this section if any.', VALUE_OPTIONAL, ), 'itemid' => new external_value( PARAM_INT, 'The optional item id delegate component can use to identify its instance.', VALUE_OPTIONAL, ), 'modules' => new external_multiple_structure( new external_single_structure( [ 'id' => new external_value(PARAM_INT, 'activity id'), 'url' => new external_value(PARAM_URL, 'activity url', VALUE_OPTIONAL), 'name' => new external_value(PARAM_RAW, 'activity module name'), 'instance' => new external_value(PARAM_INT, 'instance id', VALUE_OPTIONAL), 'contextid' => new external_value(PARAM_INT, 'Activity context id.', VALUE_OPTIONAL), 'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL), 'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL), 'uservisible' => new external_value( PARAM_BOOL, 'Is the module visible for the user?', VALUE_OPTIONAL, ), 'availabilityinfo' => new external_value( PARAM_RAW, 'Availability information.', VALUE_OPTIONAL, ), 'visibleoncoursepage' => new external_value( PARAM_INT, 'is the module visible on course page', VALUE_OPTIONAL, ), 'modicon' => new external_value(PARAM_URL, 'activity icon url'), 'modname' => new external_value(PARAM_PLUGIN, 'activity module type'), 'purpose' => new external_value(PARAM_ALPHA, 'the module purpose'), 'branded' => new external_value( PARAM_BOOL, 'Whether the module is branded or not', VALUE_OPTIONAL, ), 'modplural' => new external_value(PARAM_TEXT, 'activity module plural name'), 'availability' => new external_value(PARAM_RAW, 'module availability settings', VALUE_OPTIONAL), 'indent' => new external_value(PARAM_INT, 'number of identation in the site'), 'onclick' => new external_value(PARAM_RAW, 'Onclick action.', VALUE_OPTIONAL), 'afterlink' => new external_value( PARAM_RAW, 'After link info to be displayed.', VALUE_OPTIONAL, ), 'activitybadge' => self::get_activitybadge_structure(), 'customdata' => new external_value(PARAM_RAW, 'Custom data (JSON encoded).', VALUE_OPTIONAL), 'noviewlink' => new external_value( PARAM_BOOL, 'Whether the module has no view page', VALUE_OPTIONAL, ), 'candisplay' => new external_value( PARAM_BOOL, 'Whether the module should be displayed on the course page', VALUE_OPTIONAL, ), 'completion' => new external_value( PARAM_INT, 'Type of completion tracking: 0 means none, 1 manual, 2 automatic.', VALUE_OPTIONAL, ), 'completiondata' => $completiondefinition, 'downloadcontent' => new external_value(PARAM_INT, 'The download content value', VALUE_OPTIONAL), 'dates' => new external_multiple_structure( new external_single_structure( [ 'label' => new external_value(PARAM_TEXT, 'date label'), 'timestamp' => new external_value(PARAM_INT, 'date timestamp'), 'relativeto' => new external_value( PARAM_INT, 'relative date timestamp', VALUE_OPTIONAL, ), 'dataid' => new external_value(PARAM_NOTAGS, 'cm data id', VALUE_OPTIONAL), ], ), 'Course dates', VALUE_DEFAULT, [], ), 'groupmode' => new external_value(PARAM_INT, 'Group mode value', VALUE_OPTIONAL), 'contents' => new external_multiple_structure( new external_single_structure( [ // Content info. 'type' => new external_value(PARAM_TEXT, 'a file or a folder or external link'), 'filename' => new external_value(PARAM_FILE, 'filename'), 'filepath' => new external_value(PARAM_PATH, 'filepath'), 'filesize' => new external_value(PARAM_INT, 'filesize'), 'fileurl' => new external_value(PARAM_URL, 'downloadable file url', VALUE_OPTIONAL), 'content' => new external_value( PARAM_RAW, 'Raw content, will be used when type is content', VALUE_OPTIONAL, ), 'timecreated' => new external_value(PARAM_INT, 'Time created'), 'timemodified' => new external_value(PARAM_INT, 'Time modified'), 'sortorder' => new external_value(PARAM_INT, 'Content sort order'), 'mimetype' => new external_value( PARAM_RAW, 'File mime type.', VALUE_OPTIONAL, ), 'isexternalfile' => new external_value( PARAM_BOOL, 'Whether is an external file.', VALUE_OPTIONAL, ), 'repositorytype' => new external_value( PARAM_PLUGIN, 'The repository type for external files.', VALUE_OPTIONAL, ), // Copyright related info. 'userid' => new external_value(PARAM_INT, 'User who added this content to moodle'), 'author' => new external_value(PARAM_TEXT, 'Content owner'), 'license' => new external_value(PARAM_TEXT, 'Content license'), 'tags' => new external_multiple_structure( \core_tag\external\tag_item_exporter::get_read_structure(), 'Tags', VALUE_OPTIONAL, ), ], ), 'Course contents', VALUE_DEFAULT, [], ), 'contentsinfo' => new external_single_structure( [ 'filescount' => new external_value(PARAM_INT, 'Total number of files.'), 'filessize' => new external_value(PARAM_INT, 'Total files size.'), 'lastmodified' => new external_value(PARAM_INT, 'Last time files were modified.'), 'mimetypes' => new external_multiple_structure( new external_value(PARAM_RAW, 'File mime type.'), 'Files mime types.' ), 'repositorytype' => new external_value( PARAM_PLUGIN, 'The repository type for the main file.', VALUE_OPTIONAL, ), ], 'Contents summary information.', VALUE_OPTIONAL, ), ], ), 'list of modules', ), ], ), ); } /** * Returns description of activitybadge data. * * @return external_description */ protected static function get_activitybadge_structure(): external_description { return new external_single_structure( [ 'badgecontent' => new external_value( PARAM_TEXT, 'The content to be displayed in the activity badge', VALUE_OPTIONAL ), 'badgestyle' => new external_value( PARAM_TEXT, 'The style for the activity badge', VALUE_OPTIONAL ), 'badgeurl' => new external_value( PARAM_URL, 'An optional URL to redirect the user when the activity badge is clicked', VALUE_OPTIONAL ), 'badgeelementid' => new external_value( PARAM_ALPHANUMEXT, 'An optional id in case the module wants to add some code for the activity badge', VALUE_OPTIONAL ), 'badgeextraattributes' => new external_multiple_structure( new external_single_structure( [ 'name' => new external_value( PARAM_TEXT, 'The attribute name', VALUE_OPTIONAL ), 'value' => new external_value( PARAM_TEXT, 'The attribute value', VALUE_OPTIONAL ), ], 'Each of the attribute names and values', VALUE_OPTIONAL ), 'An optional array of extra HTML attributes to add to the badge element', VALUE_OPTIONAL ), ], 'Activity badge to display near the name', VALUE_OPTIONAL ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.3 */ public static function get_courses_parameters() { return new external_function_parameters( array('options' => new external_single_structure( array('ids' => new external_multiple_structure( new external_value(PARAM_INT, 'Course id') , 'List of course id. If empty return all courses except front page course.', VALUE_OPTIONAL) ), 'options - operator OR is used', VALUE_DEFAULT, array()) ) ); } /** * Get courses * * @param array $options It contains an array (list of ids) * @return array * @since Moodle 2.2 */ public static function get_courses($options = array()) { global $CFG, $DB; require_once($CFG->dirroot . "/course/lib.php"); //validate parameter $params = self::validate_parameters(self::get_courses_parameters(), array('options' => $options)); //retrieve courses if (!array_key_exists('ids', $params['options']) or empty($params['options']['ids'])) { $courses = $DB->get_records('course'); } else { $courses = $DB->get_records_list('course', 'id', $params['options']['ids']); } //create return value $coursesinfo = array(); foreach ($courses as $course) { // now security checks $context = context_course::instance($course->id, IGNORE_MISSING); $courseformatoptions = course_get_format($course)->get_format_options(); try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $course->id; throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam); } if ($course->id != SITEID) { require_capability('moodle/course:view', $context); } $courseinfo = array(); $courseinfo['id'] = $course->id; $courseinfo['fullname'] = \core_external\util::format_string($course->fullname, $context); $courseinfo['shortname'] = \core_external\util::format_string($course->shortname, $context); $courseinfo['displayname'] = \core_external\util::format_string(get_course_display_name_for_list($course), $context); $courseinfo['categoryid'] = $course->category; list($courseinfo['summary'], $courseinfo['summaryformat']) = \core_external\util::format_text($course->summary, $course->summaryformat, $context, 'course', 'summary', 0); $courseinfo['format'] = $course->format; $courseinfo['startdate'] = $course->startdate; $courseinfo['enddate'] = $course->enddate; $courseinfo['showactivitydates'] = $course->showactivitydates; $courseinfo['showcompletionconditions'] = $course->showcompletionconditions; if (array_key_exists('numsections', $courseformatoptions)) { // For backward-compartibility $courseinfo['numsections'] = $courseformatoptions['numsections']; } $courseinfo['pdfexportfont'] = $course->pdfexportfont; $handler = core_course\customfield\course_handler::create(); if ($customfields = $handler->export_instance_data($course->id)) { $courseinfo['customfields'] = []; foreach ($customfields as $data) { $courseinfo['customfields'][] = [ 'type' => $data->get_type(), 'value' => $data->get_value(), 'valueraw' => $data->get_data_controller()->get_value(), 'name' => $data->get_name(), 'shortname' => $data->get_shortname() ]; } } //some field should be returned only if the user has update permission $courseadmin = has_capability('moodle/course:update', $context); if ($courseadmin) { $courseinfo['categorysortorder'] = $course->sortorder; $courseinfo['idnumber'] = $course->idnumber; $courseinfo['showgrades'] = $course->showgrades; $courseinfo['showreports'] = $course->showreports; $courseinfo['newsitems'] = $course->newsitems; $courseinfo['visible'] = $course->visible; $courseinfo['maxbytes'] = $course->maxbytes; if (array_key_exists('hiddensections', $courseformatoptions)) { // For backward-compartibility $courseinfo['hiddensections'] = $courseformatoptions['hiddensections']; } // Return numsections for backward-compatibility with clients who expect it. $courseinfo['numsections'] = course_get_format($course)->get_last_section_number(); $courseinfo['groupmode'] = $course->groupmode; $courseinfo['groupmodeforce'] = $course->groupmodeforce; $courseinfo['defaultgroupingid'] = $course->defaultgroupingid; $courseinfo['lang'] = clean_param($course->lang, PARAM_LANG); $courseinfo['timecreated'] = $course->timecreated; $courseinfo['timemodified'] = $course->timemodified; $courseinfo['forcetheme'] = clean_param($course->theme, PARAM_THEME); $courseinfo['enablecompletion'] = $course->enablecompletion; $courseinfo['completionnotify'] = $course->completionnotify; $courseinfo['courseformatoptions'] = array(); foreach ($courseformatoptions as $key => $value) { $courseinfo['courseformatoptions'][] = array( 'name' => $key, 'value' => $value ); } } if ($courseadmin or $course->visible or has_capability('moodle/course:viewhiddencourses', $context)) { $coursesinfo[] = $courseinfo; } } return $coursesinfo; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.2 */ public static function get_courses_returns() { return new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'course id'), 'shortname' => new external_value(PARAM_RAW, 'course short name'), 'categoryid' => new external_value(PARAM_INT, 'category id'), 'categorysortorder' => new external_value(PARAM_INT, 'sort order into the category', VALUE_OPTIONAL), 'fullname' => new external_value(PARAM_RAW, 'full name'), 'displayname' => new external_value(PARAM_RAW, 'course display name'), 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL), 'summary' => new external_value(PARAM_RAW, 'summary'), 'summaryformat' => new external_format_value('summary'), 'format' => new external_value(PARAM_PLUGIN, 'course format: weeks, topics, social, site,..'), 'showgrades' => new external_value(PARAM_INT, '1 if grades are shown, otherwise 0', VALUE_OPTIONAL), 'newsitems' => new external_value(PARAM_INT, 'number of recent items appearing on the course page', VALUE_OPTIONAL), 'startdate' => new external_value(PARAM_INT, 'timestamp when the course start'), 'enddate' => new external_value(PARAM_INT, 'timestamp when the course end'), 'numsections' => new external_value(PARAM_INT, '(deprecated, use courseformatoptions) number of weeks/topics', VALUE_OPTIONAL), 'maxbytes' => new external_value(PARAM_INT, 'largest size of file that can be uploaded into the course', VALUE_OPTIONAL), 'showreports' => new external_value(PARAM_INT, 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL), 'visible' => new external_value(PARAM_INT, '1: available to student, 0:not available', VALUE_OPTIONAL), 'hiddensections' => new external_value(PARAM_INT, '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students', VALUE_OPTIONAL), 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL), 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL), 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL), 'timecreated' => new external_value(PARAM_INT, 'timestamp when the course have been created', VALUE_OPTIONAL), 'timemodified' => new external_value(PARAM_INT, 'timestamp when the course have been modified', VALUE_OPTIONAL), 'enablecompletion' => new external_value(PARAM_INT, 'Enabled, control via completion and activity settings. Disbaled, not shown in activity settings.', VALUE_OPTIONAL), 'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL), 'lang' => new external_value(PARAM_SAFEDIR, 'forced course language', VALUE_OPTIONAL), 'forcetheme' => new external_value(PARAM_PLUGIN, 'name of the force theme', VALUE_OPTIONAL), 'courseformatoptions' => new external_multiple_structure( new external_single_structure( array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'), 'value' => new external_value(PARAM_RAW, 'course format option value') )), 'additional options for particular course format', 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'), 'customfields' => new external_multiple_structure( new external_single_structure( ['name' => new external_value(PARAM_RAW, 'The name of the custom field'), 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'The shortname of the custom field'), 'type' => new external_value(PARAM_COMPONENT, 'The type of the custom field - text, checkbox...'), 'valueraw' => new external_value(PARAM_RAW, 'The raw value of the custom field'), 'value' => new external_value(PARAM_RAW, 'The value of the custom field')] ), 'Custom fields and associated values', VALUE_OPTIONAL), ), 'course' ) ); } /** * Return array of all editable course custom fields indexed by their shortname * * @param \context $context * @param int $courseid * @return \core_customfield\field_controller[] */ public static function get_editable_customfields(\context $context, int $courseid = 0): array { $result = []; $handler = \core_course\customfield\course_handler::create(); $handler->set_parent_context($context); foreach ($handler->get_editable_fields($courseid) as $field) { $result[$field->get('shortname')] = $field; } return $result; } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.2 */ public static function create_courses_parameters() { $courseconfig = get_config('moodlecourse'); //needed for many default values return new external_function_parameters( array( 'courses' => new external_multiple_structure( new external_single_structure( array( 'fullname' => new external_value(PARAM_TEXT, 'full name'), 'shortname' => new external_value(PARAM_TEXT, 'course short name'), 'categoryid' => new external_value(PARAM_INT, 'category id'), 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL), 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL), 'summaryformat' => new external_format_value('summary', VALUE_DEFAULT), 'format' => new external_value(PARAM_PLUGIN, 'course format: weeks, topics, social, site,..', VALUE_DEFAULT, $courseconfig->format), 'showgrades' => new external_value(PARAM_INT, '1 if grades are shown, otherwise 0', VALUE_DEFAULT, $courseconfig->showgrades), 'newsitems' => new external_value(PARAM_INT, 'number of recent items appearing on the course page', VALUE_DEFAULT, $courseconfig->newsitems), '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), 'numsections' => new external_value(PARAM_INT, '(deprecated, use courseformatoptions) number of weeks/topics', VALUE_OPTIONAL), 'maxbytes' => new external_value(PARAM_INT, 'largest size of file that can be uploaded into the course', VALUE_DEFAULT, $courseconfig->maxbytes), 'showreports' => new external_value(PARAM_INT, 'are activity report shown (yes = 1, no =0)', VALUE_DEFAULT, $courseconfig->showreports), 'visible' => new external_value( PARAM_INT, '1: available to student, 0: not available', VALUE_DEFAULT, $courseconfig->visible, ), 'hiddensections' => new external_value(PARAM_INT, '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students', VALUE_OPTIONAL), 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_DEFAULT, $courseconfig->groupmode), 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_DEFAULT, $courseconfig->groupmodeforce), 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_DEFAULT, 0), 'enablecompletion' => new external_value(PARAM_INT, 'Enabled, control via completion and activity settings. Disabled, not shown in activity settings.', VALUE_OPTIONAL), 'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL), 'lang' => new external_value(PARAM_SAFEDIR, 'forced course language', VALUE_OPTIONAL), 'forcetheme' => new external_value(PARAM_PLUGIN, 'name of the force theme', VALUE_OPTIONAL), 'courseformatoptions' => new external_multiple_structure( new external_single_structure( array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'), 'value' => new external_value(PARAM_RAW, 'course format option value') )), 'additional options for particular course format', VALUE_OPTIONAL), 'customfields' => new external_multiple_structure( new external_single_structure( array( 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'The shortname of the custom field'), 'value' => new external_value(PARAM_RAW, 'The value of the custom field'), )), 'custom fields for the course', VALUE_OPTIONAL ) )), 'courses to create' ) ) ); } /** * Create courses * * @param array $courses * @return array courses (id and shortname only) * @since Moodle 2.2 */ public static function create_courses($courses) { global $CFG, $DB; require_once($CFG->dirroot . "/course/lib.php"); require_once($CFG->libdir . '/completionlib.php'); $params = self::validate_parameters(self::create_courses_parameters(), array('courses' => $courses)); $courseconfig = get_config('moodlecourse'); $availablethemes = core_component::get_plugin_list('theme'); $availablelangs = get_string_manager()->get_list_of_translations(); $transaction = $DB->start_delegated_transaction(); foreach ($params['courses'] as $course) { // Ensure the current user is allowed to run this function $context = context_coursecat::instance($course['categoryid'], IGNORE_MISSING); try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->catid = $course['categoryid']; throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam); } require_capability('moodle/course:create', $context); // Fullname and short name are required to be non-empty. if (trim($course['fullname']) === '') { throw new moodle_exception('errorinvalidparam', 'webservice', '', 'fullname'); } else if (trim($course['shortname']) === '') { throw new moodle_exception('errorinvalidparam', 'webservice', '', 'shortname'); } // Make sure start/end date are correctly set. if (!array_key_exists('startdate', $course)) { $course['startdate'] = usergetmidnight(time()); } if (!array_key_exists('enddate', $course) && $courseconfig->courseenddateenabled) { $course['enddate'] = $course['startdate'] + $courseconfig->courseduration; } // Make sure lang is valid if (array_key_exists('lang', $course)) { if (empty($availablelangs[$course['lang']])) { throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang'); } if (!has_capability('moodle/course:setforcedlanguage', $context)) { unset($course['lang']); } } // Make sure theme is valid if (array_key_exists('forcetheme', $course)) { if (!empty($CFG->allowcoursethemes)) { if (empty($availablethemes[$course['forcetheme']])) { throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme'); } else { $course['theme'] = $course['forcetheme']; } } } $course['showactivitydates'] = $courseconfig->showactivitydates; //force visibility if ws user doesn't have the permission to set it $category = $DB->get_record('course_categories', array('id' => $course['categoryid'])); if (!has_capability('moodle/course:visibility', $context)) { $course['visible'] = $category->visible; } //set default value for completion if (completion_info::is_enabled_for_site()) { if (!array_key_exists('enablecompletion', $course)) { $course['enablecompletion'] = $courseconfig->enablecompletion; } } else { $course['enablecompletion'] = 0; } $course['category'] = $course['categoryid']; // Summary format. $course['summaryformat'] = util::validate_format($course['summaryformat']); if (!empty($course['courseformatoptions'])) { foreach ($course['courseformatoptions'] as $option) { $course[$option['name']] = $option['value']; } } // Custom fields. if (!empty($course['customfields'])) { $customfields = self::get_editable_customfields($context); foreach ($course['customfields'] as $field) { if (array_key_exists($field['shortname'], $customfields)) { // Ensure we're populating the element form fields correctly. $controller = \core_customfield\data_controller::create(0, null, $customfields[$field['shortname']]); $course[$controller->get_form_element_name()] = $field['value']; } } } //Note: create_course() core function check shortname, idnumber, category $course['id'] = create_course((object) $course)->id; $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']); } $transaction->allow_commit(); return $resultcourses; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.2 */ public static function create_courses_returns() { return new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'course id'), 'shortname' => new external_value(PARAM_RAW, 'short name'), ) ) ); } /** * Update courses * * @return external_function_parameters * @since Moodle 2.5 */ public static function update_courses_parameters() { return new external_function_parameters( array( 'courses' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'ID of the course'), 'fullname' => new external_value(PARAM_TEXT, 'full name', VALUE_OPTIONAL), 'shortname' => new external_value(PARAM_TEXT, 'course short name', VALUE_OPTIONAL), 'categoryid' => new external_value(PARAM_INT, 'category id', VALUE_OPTIONAL), 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL), '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), 'showgrades' => new external_value(PARAM_INT, '1 if grades are shown, otherwise 0', VALUE_OPTIONAL), 'newsitems' => new external_value(PARAM_INT, 'number of recent items appearing on the course page', 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), 'numsections' => new external_value(PARAM_INT, '(deprecated, use courseformatoptions) number of weeks/topics', VALUE_OPTIONAL), 'maxbytes' => new external_value(PARAM_INT, 'largest size of file that can be uploaded into the course', VALUE_OPTIONAL), 'showreports' => new external_value(PARAM_INT, 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL), 'visible' => new external_value(PARAM_INT, '1: available to student, 0:not available', VALUE_OPTIONAL), 'hiddensections' => new external_value(PARAM_INT, '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students', VALUE_OPTIONAL), 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL), 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL), 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL), 'enablecompletion' => new external_value(PARAM_INT, 'Enabled, control via completion and activity settings. Disabled, not shown in activity settings.', VALUE_OPTIONAL), 'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL), 'lang' => new external_value(PARAM_SAFEDIR, 'forced course language', VALUE_OPTIONAL), 'forcetheme' => new external_value(PARAM_PLUGIN, 'name of the force theme', VALUE_OPTIONAL), 'courseformatoptions' => new external_multiple_structure( new external_single_structure( array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'), 'value' => new external_value(PARAM_RAW, 'course format option value') )), 'additional options for particular course format', VALUE_OPTIONAL), 'customfields' => new external_multiple_structure( new external_single_structure( [ 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'The shortname of the custom field'), 'value' => new external_value(PARAM_RAW, 'The value of the custom field') ] ), 'Custom fields', VALUE_OPTIONAL), ) ), 'courses to update' ) ) ); } /** * Update courses * * @param array $courses * @since Moodle 2.5 */ public static function update_courses($courses) { global $CFG, $DB; require_once($CFG->dirroot . "/course/lib.php"); $warnings = array(); $params = self::validate_parameters(self::update_courses_parameters(), array('courses' => $courses)); $availablethemes = core_component::get_plugin_list('theme'); $availablelangs = get_string_manager()->get_list_of_translations(); foreach ($params['courses'] as $course) { // Catch any exception while updating course and return as warning to user. try { // Ensure the current user is allowed to run this function. $context = context_course::instance($course['id'], MUST_EXIST); self::validate_context($context); $oldcourse = course_get_format($course['id'])->get_course(); require_capability('moodle/course:update', $context); // Check if user can change category. if (array_key_exists('categoryid', $course) && ($oldcourse->category != $course['categoryid'])) { require_capability('moodle/course:changecategory', $context); $course['category'] = $course['categoryid']; } // Check if the user can change fullname, and the new value is non-empty. if (array_key_exists('fullname', $course) && ($oldcourse->fullname != $course['fullname'])) { require_capability('moodle/course:changefullname', $context); if (trim($course['fullname']) === '') { throw new moodle_exception('errorinvalidparam', 'webservice', '', 'fullname'); } } // Check if the user can change shortname, and the new value is non-empty. if (array_key_exists('shortname', $course) && ($oldcourse->shortname != $course['shortname'])) { require_capability('moodle/course:changeshortname', $context); if (trim($course['shortname']) === '') { throw new moodle_exception('errorinvalidparam', 'webservice', '', 'shortname'); } } // Check if the user can change the idnumber. if (array_key_exists('idnumber', $course) && ($oldcourse->idnumber != $course['idnumber'])) { require_capability('moodle/course:changeidnumber', $context); } // Check if user can change summary. if (array_key_exists('summary', $course) && ($oldcourse->summary != $course['summary'])) { require_capability('moodle/course:changesummary', $context); } // Summary format. if (array_key_exists('summaryformat', $course) && ($oldcourse->summaryformat != $course['summaryformat'])) { require_capability('moodle/course:changesummary', $context); $course['summaryformat'] = util::validate_format($course['summaryformat']); } // Check if user can change visibility. if (array_key_exists('visible', $course) && ($oldcourse->visible != $course['visible'])) { require_capability('moodle/course:visibility', $context); } // Make sure lang is valid. if (array_key_exists('lang', $course) && ($oldcourse->lang != $course['lang'])) { require_capability('moodle/course:setforcedlanguage', $context); if (empty($availablelangs[$course['lang']])) { throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang'); } } // Make sure theme is valid. if (array_key_exists('forcetheme', $course)) { if (!empty($CFG->allowcoursethemes)) { if (empty($availablethemes[$course['forcetheme']])) { throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme'); } else { $course['theme'] = $course['forcetheme']; } } } // Make sure completion is enabled before setting it. if (array_key_exists('enabledcompletion', $course) && !completion_info::is_enabled_for_site()) { $course['enabledcompletion'] = 0; } // Make sure maxbytes are less then CFG->maxbytes. if (array_key_exists('maxbytes', $course)) { // We allow updates back to 0 max bytes, a special value denoting the course uses the site limit. // Otherwise, either use the size specified, or cap at the max size for the course. if ($course['maxbytes'] != 0) { $course['maxbytes'] = get_max_upload_file_size($CFG->maxbytes, $course['maxbytes']); } } if (!empty($course['courseformatoptions'])) { foreach ($course['courseformatoptions'] as $option) { if (isset($option['name']) && isset($option['value'])) { $course[$option['name']] = $option['value']; } } } // Custom fields. if (isset($course['customfields'])) { $customfields = self::get_editable_customfields($context, $course['id']); foreach ($course['customfields'] as $field) { if (array_key_exists($field['shortname'], $customfields)) { // Ensure we're populating the element form fields correctly. $controller = \core_customfield\data_controller::create(0, null, $customfields[$field['shortname']]); $course[$controller->get_form_element_name()] = $field['value']; } } } // Update course if user has all required capabilities. update_course((object) $course); } catch (Exception $e) { $warning = array(); $warning['item'] = 'course'; $warning['itemid'] = $course['id']; if ($e instanceof moodle_exception) { $warning['warningcode'] = $e->errorcode; } else { $warning['warningcode'] = $e->getCode(); } $warning['message'] = $e->getMessage(); $warnings[] = $warning; } } $result = array(); $result['warnings'] = $warnings; return $result; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.5 */ public static function update_courses_returns() { return new external_single_structure( array( 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.2 */ public static function delete_courses_parameters() { return new external_function_parameters( array( 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID')), ) ); } /** * Delete courses * * @param array $courseids A list of course ids * @since Moodle 2.2 */ public static function delete_courses($courseids) { global $CFG, $DB; require_once($CFG->dirroot."/course/lib.php"); // Parameter validation. $params = self::validate_parameters(self::delete_courses_parameters(), array('courseids'=>$courseids)); $warnings = array(); foreach ($params['courseids'] as $courseid) { $course = $DB->get_record('course', array('id' => $courseid)); if ($course === false) { $warnings[] = array( 'item' => 'course', 'itemid' => $courseid, 'warningcode' => 'unknowncourseidnumber', 'message' => 'Unknown course ID ' . $courseid ); continue; } // Check if the context is valid. $coursecontext = context_course::instance($course->id); self::validate_context($coursecontext); // Check if the current user has permission. if (!can_delete_course($courseid)) { $warnings[] = array( 'item' => 'course', 'itemid' => $courseid, 'warningcode' => 'cannotdeletecourse', 'message' => 'You do not have the permission to delete this course' . $courseid ); continue; } if (delete_course($course, false) === false) { $warnings[] = array( 'item' => 'course', 'itemid' => $courseid, 'warningcode' => 'cannotdeletecategorycourse', 'message' => 'Course ' . $courseid . ' failed to be deleted' ); continue; } } fix_course_sortorder(); return array('warnings' => $warnings); } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.2 */ public static function delete_courses_returns() { return new external_single_structure( array( 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.3 */ public static function duplicate_course_parameters() { return new external_function_parameters( array( 'courseid' => new external_value(PARAM_INT, 'course to duplicate id'), 'fullname' => new external_value(PARAM_TEXT, 'duplicated course full name'), 'shortname' => new external_value(PARAM_TEXT, 'duplicated course short name'), 'categoryid' => new external_value(PARAM_INT, 'duplicated course category parent'), 'visible' => new external_value(PARAM_INT, 'duplicated course visible, default to yes', VALUE_DEFAULT, 1), 'options' => new external_multiple_structure( new external_single_structure( array( 'name' => new external_value(PARAM_ALPHAEXT, 'The backup option name: "activities" (int) Include course activites (default to 1 that is equal to yes), "blocks" (int) Include course blocks (default to 1 that is equal to yes), "filters" (int) Include course filters (default to 1 that is equal to yes), "users" (int) Include users (default to 0 that is equal to no), "enrolments" (int) Include enrolment methods (default to 1 - restore only with users), "role_assignments" (int) Include role assignments (default to 0 that is equal to no), "comments" (int) Include user comments (default to 0 that is equal to no), "userscompletion" (int) Include user course completion information (default to 0 that is equal to no), "logs" (int) Include course logs (default to 0 that is equal to no), "grade_histories" (int) Include histories (default to 0 that is equal to no)' ), 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)' ) ) ), 'Course duplication options', VALUE_DEFAULT, array() ), ) ); } /** * Duplicate a course * * @param int $courseid * @param string $fullname Duplicated course fullname * @param string $shortname Duplicated course shortname * @param int $categoryid Duplicated course parent category id * @param int $visible Duplicated course availability * @param array $options List of backup options * @return array New course info * @since Moodle 2.3 */ public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) { global $CFG, $USER, $DB; require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php'); require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); // Parameter validation. $params = self::validate_parameters( self::duplicate_course_parameters(), array( 'courseid' => $courseid, 'fullname' => $fullname, 'shortname' => $shortname, 'categoryid' => $categoryid, 'visible' => $visible, 'options' => $options ) ); // Context validation. if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) { throw new moodle_exception('invalidcourseid', 'error'); } // Category where duplicated course is going to be created. $categorycontext = context_coursecat::instance($params['categoryid']); self::validate_context($categorycontext); // Course to be duplicated. $coursecontext = context_course::instance($course->id); self::validate_context($coursecontext); $backupdefaults = array( 'activities' => 1, 'blocks' => 1, 'filters' => 1, 'users' => 0, 'enrolments' => backup::ENROL_WITHUSERS, 'role_assignments' => 0, 'comments' => 0, 'userscompletion' => 0, 'logs' => 0, 'grade_histories' => 0 ); $backupsettings = array(); // Check for backup and restore options. if (!empty($params['options'])) { foreach ($params['options'] as $option) { // Strict check for a correct value (allways 1 or 0, true or false). $value = clean_param($option['value'], PARAM_INT); if ($value !== 0 and $value !== 1) { throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']); } if (!isset($backupdefaults[$option['name']])) { throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']); } $backupsettings[$option['name']] = $value; } } // Capability checking. // The backup controller check for this currently, this may be redundant. require_capability('moodle/course:create', $categorycontext); require_capability('moodle/restore:restorecourse', $categorycontext); require_capability('moodle/backup:backupcourse', $coursecontext); if (!empty($backupsettings['users'])) { require_capability('moodle/backup:userinfo', $coursecontext); require_capability('moodle/restore:userinfo', $categorycontext); } // Check if the shortname is used. if ($foundcourses = $DB->get_records('course', array('shortname'=>$shortname))) { foreach ($foundcourses as $foundcourse) { $foundcoursenames[] = $foundcourse->fullname; } $foundcoursenamestring = implode(',', $foundcoursenames); throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring); } // Backup the course. $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id); foreach ($backupsettings as $name => $value) { if ($setting = $bc->get_plan()->get_setting($name)) { $bc->get_plan()->get_setting($name)->set_value($value); } } $backupid = $bc->get_backupid(); $backupbasepath = $bc->get_plan()->get_basepath(); $bc->execute_plan(); $results = $bc->get_results(); $file = $results['backup_destination']; $bc->destroy(); // Restore the backup immediately. // Check if we need to unzip the file because the backup temp dir does not contains backup files. if (!file_exists($backupbasepath . "/moodle_backup.xml")) { $file->extract_to_pathname(get_file_packer('application/vnd.moodle.backup'), $backupbasepath); } // Create new course. $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']); $rc = new restore_controller($backupid, $newcourseid, backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE); foreach ($backupsettings as $name => $value) { $setting = $rc->get_plan()->get_setting($name); if ($setting->get_status() == backup_setting::NOT_LOCKED) { $setting->set_value($value); } } if (!$rc->execute_precheck()) { $precheckresults = $rc->get_precheck_results(); if (is_array($precheckresults) && !empty($precheckresults['errors'])) { if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backupbasepath); } $errorinfo = ''; foreach ($precheckresults['errors'] as $error) { $errorinfo .= $error; } if (array_key_exists('warnings', $precheckresults)) { foreach ($precheckresults['warnings'] as $warning) { $errorinfo .= $warning; } } throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo); } } $rc->execute_plan(); $rc->destroy(); $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST); $course->fullname = $params['fullname']; $course->shortname = $params['shortname']; $course->visible = $params['visible']; // Set shortname and fullname back. $DB->update_record('course', $course); if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backupbasepath); } // Delete the course backup file created by this WebService. Originally located in the course backups area. $file->delete(); return array('id' => $course->id, 'shortname' => $course->shortname); } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.3 */ public static function duplicate_course_returns() { return new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'course id'), 'shortname' => new external_value(PARAM_RAW, 'short name'), ) ); } /** * Returns description of method parameters for import_course * * @return external_function_parameters * @since Moodle 2.4 */ public static function import_course_parameters() { return new external_function_parameters( array( 'importfrom' => new external_value(PARAM_INT, 'the id of the course we are importing from'), 'importto' => new external_value(PARAM_INT, 'the id of the course we are importing to'), 'deletecontent' => new external_value(PARAM_INT, 'whether to delete the course content where we are importing to (default to 0 = No)', VALUE_DEFAULT, 0), 'options' => new external_multiple_structure( new external_single_structure( array( 'name' => new external_value(PARAM_ALPHA, 'The backup option name: "activities" (int) Include course activites (default to 1 that is equal to yes), "blocks" (int) Include course blocks (default to 1 that is equal to yes), "filters" (int) Include course filters (default to 1 that is equal to yes)' ), 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)' ) ) ), 'Course import options', VALUE_DEFAULT, array() ), ) ); } /** * Imports a course * * @param int $importfrom The id of the course we are importing from * @param int $importto The id of the course we are importing to * @param bool $deletecontent Whether to delete the course we are importing to content * @param array $options List of backup options * @return null * @since Moodle 2.4 */ public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) { global $CFG, $USER, $DB; require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php'); require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); // Parameter validation. $params = self::validate_parameters( self::import_course_parameters(), array( 'importfrom' => $importfrom, 'importto' => $importto, 'deletecontent' => $deletecontent, 'options' => $options ) ); if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) { throw new moodle_exception('invalidextparam', 'webservice', '', $params['deletecontent']); } // Context validation. if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) { throw new moodle_exception('invalidcourseid', 'error'); } if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) { throw new moodle_exception('invalidcourseid', 'error'); } $importfromcontext = context_course::instance($importfrom->id); self::validate_context($importfromcontext); $importtocontext = context_course::instance($importto->id); self::validate_context($importtocontext); $backupdefaults = array( 'activities' => 1, 'blocks' => 1, 'filters' => 1 ); $backupsettings = array(); // Check for backup and restore options. if (!empty($params['options'])) { foreach ($params['options'] as $option) { // Strict check for a correct value (allways 1 or 0, true or false). $value = clean_param($option['value'], PARAM_INT); if ($value !== 0 and $value !== 1) { throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']); } if (!isset($backupdefaults[$option['name']])) { throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']); } $backupsettings[$option['name']] = $value; } } // Capability checking. require_capability('moodle/backup:backuptargetimport', $importfromcontext); require_capability('moodle/restore:restoretargetimport', $importtocontext); $bc = new backup_controller(backup::TYPE_1COURSE, $importfrom->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id); foreach ($backupsettings as $name => $value) { $bc->get_plan()->get_setting($name)->set_value($value); } $backupid = $bc->get_backupid(); $backupbasepath = $bc->get_plan()->get_basepath(); $bc->execute_plan(); $bc->destroy(); // Restore the backup immediately. // Check if we must delete the contents of the destination course. if ($params['deletecontent']) { $restoretarget = backup::TARGET_EXISTING_DELETING; } else { $restoretarget = backup::TARGET_EXISTING_ADDING; } $rc = new restore_controller($backupid, $importto->id, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, $restoretarget); foreach ($backupsettings as $name => $value) { $rc->get_plan()->get_setting($name)->set_value($value); } if (!$rc->execute_precheck()) { $precheckresults = $rc->get_precheck_results(); if (is_array($precheckresults) && !empty($precheckresults['errors'])) { if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backupbasepath); } $errorinfo = ''; foreach ($precheckresults['errors'] as $error) { $errorinfo .= $error; } if (array_key_exists('warnings', $precheckresults)) { foreach ($precheckresults['warnings'] as $warning) { $errorinfo .= $warning; } } throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo); } } else { if ($restoretarget == backup::TARGET_EXISTING_DELETING) { restore_dbops::delete_course_content($importto->id); } } $rc->execute_plan(); $rc->destroy(); if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backupbasepath); } return null; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.4 */ public static function import_course_returns() { return null; } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.3 */ public static function get_categories_parameters() { return new external_function_parameters( array( 'criteria' => new external_multiple_structure( new external_single_structure( array( 'key' => new external_value(PARAM_ALPHA, 'The category column to search, expected keys (value format) are:'. '"id" (int) the category id,'. '"ids" (string) category ids separated by commas,'. '"name" (string) the category name,'. '"parent" (int) the parent category id,'. '"idnumber" (string) category idnumber'. ' - user must have \'moodle/category:manage\' to search on idnumber,'. '"visible" (int) whether the returned categories must be visible or hidden. If the key is not passed, then the function return all categories that the user can see.'. ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'. '"theme" (string) only return the categories having this theme'. ' - user must have \'moodle/category:manage\' to search on theme'), 'value' => new external_value(PARAM_RAW, 'the value to match') ) ), 'criteria', VALUE_DEFAULT, array() ), 'addsubcategories' => new external_value(PARAM_BOOL, 'return the sub categories infos (1 - default) otherwise only the category info (0)', VALUE_DEFAULT, 1) ) ); } /** * Get categories * * @param array $criteria Criteria to match the results * @param booln $addsubcategories obtain only the category (false) or its subcategories (true - default) * @return array list of categories * @since Moodle 2.3 */ public static function get_categories($criteria = array(), $addsubcategories = true) { global $CFG, $DB; require_once($CFG->dirroot . "/course/lib.php"); // Validate parameters. $params = self::validate_parameters(self::get_categories_parameters(), array('criteria' => $criteria, 'addsubcategories' => $addsubcategories)); // Retrieve the categories. $categories = array(); if (!empty($params['criteria'])) { $conditions = array(); $wheres = array(); foreach ($params['criteria'] as $crit) { $key = trim($crit['key']); // Trying to avoid duplicate keys. if (!isset($conditions[$key])) { $context = context_system::instance(); $value = null; switch ($key) { case 'id': $value = clean_param($crit['value'], PARAM_INT); $conditions[$key] = $value; $wheres[] = $key . " = :" . $key; break; case 'ids': $value = clean_param($crit['value'], PARAM_SEQUENCE); $ids = explode(',', $value); list($sqlids, $paramids) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED); $conditions = array_merge($conditions, $paramids); $wheres[] = 'id ' . $sqlids; break; case 'idnumber': if (has_capability('moodle/category:manage', $context)) { $value = clean_param($crit['value'], PARAM_RAW); $conditions[$key] = $value; $wheres[] = $key . " = :" . $key; } else { // We must throw an exception. // Otherwise the dev client would think no idnumber exists. throw new moodle_exception('criteriaerror', 'webservice', '', null, 'You don\'t have the permissions to search on the "idnumber" field.'); } break; case 'name': $value = clean_param($crit['value'], PARAM_TEXT); $conditions[$key] = $value; $wheres[] = $key . " = :" . $key; break; case 'parent': $value = clean_param($crit['value'], PARAM_INT); $conditions[$key] = $value; $wheres[] = $key . " = :" . $key; break; case 'visible': if (has_capability('moodle/category:viewhiddencategories', $context)) { $value = clean_param($crit['value'], PARAM_INT); $conditions[$key] = $value; $wheres[] = $key . " = :" . $key; } else { throw new moodle_exception('criteriaerror', 'webservice', '', null, 'You don\'t have the permissions to search on the "visible" field.'); } break; case 'theme': if (has_capability('moodle/category:manage', $context)) { $value = clean_param($crit['value'], PARAM_THEME); $conditions[$key] = $value; $wheres[] = $key . " = :" . $key; } else { throw new moodle_exception('criteriaerror', 'webservice', '', null, 'You don\'t have the permissions to search on the "theme" field.'); } break; default: throw new moodle_exception('criteriaerror', 'webservice', '', null, 'You can not search on this criteria: ' . $key); } } } if (!empty($wheres)) { $wheres = implode(" AND ", $wheres); $categories = $DB->get_records_select('course_categories', $wheres, $conditions); // Retrieve its sub subcategories (all levels). if ($categories and !empty($params['addsubcategories'])) { $newcategories = array(); // Check if we required visible/theme checks. $additionalselect = ''; $additionalparams = array(); if (isset($conditions['visible'])) { $additionalselect .= ' AND visible = :visible'; $additionalparams['visible'] = $conditions['visible']; } if (isset($conditions['theme'])) { $additionalselect .= ' AND theme= :theme'; $additionalparams['theme'] = $conditions['theme']; } foreach ($categories as $category) { $sqlselect = $DB->sql_like('path', ':path') . $additionalselect; $sqlparams = array('path' => $category->path.'/%') + $additionalparams; // It will NOT include the specified category. $subcategories = $DB->get_records_select('course_categories', $sqlselect, $sqlparams); $newcategories = $newcategories + $subcategories; // Both arrays have integer as keys. } $categories = $categories + $newcategories; } } } else { // Retrieve all categories in the database. $categories = $DB->get_records('course_categories'); } // The not returned categories. key => category id, value => reason of exclusion. $excludedcats = array(); // The returned categories. $categoriesinfo = array(); // We need to sort the categories by path. // The parent cats need to be checked by the algo first. usort($categories, "core_course_external::compare_categories_by_path"); foreach ($categories as $category) { // Check if the category is a child of an excluded category, if yes exclude it too (excluded => do not return). $parents = explode('/', $category->path); unset($parents[0]); // First key is always empty because path start with / => /1/2/4. foreach ($parents as $parentid) { // Note: when the parent exclusion was due to the context, // the sub category could still be returned. if (isset($excludedcats[$parentid]) and $excludedcats[$parentid] != 'context') { $excludedcats[$category->id] = 'parent'; } } // Check the user can use the category context. $context = context_coursecat::instance($category->id); try { self::validate_context($context); } catch (Exception $e) { $excludedcats[$category->id] = 'context'; // If it was the requested category then throw an exception. if (isset($params['categoryid']) && $category->id == $params['categoryid']) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->catid = $category->id; throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam); } } // Return the category information. if (!isset($excludedcats[$category->id])) { // Final check to see if the category is visible to the user. if (core_course_category::can_view_category($category)) { $categoryinfo = array(); $categoryinfo['id'] = $category->id; $categoryinfo['name'] = \core_external\util::format_string($category->name, $context); list($categoryinfo['description'], $categoryinfo['descriptionformat']) = \core_external\util::format_text($category->description, $category->descriptionformat, $context, 'coursecat', 'description', null); $categoryinfo['parent'] = $category->parent; $categoryinfo['sortorder'] = $category->sortorder; $categoryinfo['coursecount'] = $category->coursecount; $categoryinfo['depth'] = $category->depth; $categoryinfo['path'] = $category->path; // Some fields only returned for admin. if (has_capability('moodle/category:manage', $context)) { $categoryinfo['idnumber'] = $category->idnumber; $categoryinfo['visible'] = $category->visible; $categoryinfo['visibleold'] = $category->visibleold; $categoryinfo['timemodified'] = $category->timemodified; $categoryinfo['theme'] = clean_param($category->theme, PARAM_THEME); } $categoriesinfo[] = $categoryinfo; } else { $excludedcats[$category->id] = 'visibility'; } } } // Sorting the resulting array so it looks a bit better for the client developer. usort($categoriesinfo, "core_course_external::compare_categories_by_sortorder"); return $categoriesinfo; } /** * Sort categories array by path * private function: only used by get_categories * * @param stdClass $category1 * @param stdClass $category2 * @return int result of strcmp * @since Moodle 2.3 */ private static function compare_categories_by_path($category1, $category2) { return strcmp($category1->path, $category2->path); } /** * Sort categories array by sortorder * private function: only used by get_categories * * @param array $category1 * @param array $category2 * @return int result of strcmp * @since Moodle 2.3 */ private static function compare_categories_by_sortorder($category1, $category2) { return strcmp($category1['sortorder'], $category2['sortorder']); } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.3 */ public static function get_categories_returns() { return new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'category id'), 'name' => new external_value(PARAM_RAW, 'category name'), 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL), 'description' => new external_value(PARAM_RAW, 'category description'), 'descriptionformat' => new external_format_value('description'), 'parent' => new external_value(PARAM_INT, 'parent category id'), 'sortorder' => new external_value(PARAM_INT, 'category sorting order'), 'coursecount' => new external_value(PARAM_INT, 'number of courses in this category'), 'visible' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL), 'visibleold' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL), 'timemodified' => new external_value(PARAM_INT, 'timestamp', VALUE_OPTIONAL), 'depth' => new external_value(PARAM_INT, 'category depth'), 'path' => new external_value(PARAM_TEXT, 'category path'), 'theme' => new external_value(PARAM_THEME, 'category theme', VALUE_OPTIONAL), ), 'List of categories' ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.3 */ public static function create_categories_parameters() { return new external_function_parameters( array( 'categories' => new external_multiple_structure( new external_single_structure( array( 'name' => new external_value(PARAM_TEXT, 'new category name'), 'parent' => new external_value(PARAM_INT, 'the parent category id inside which the new category will be created - set to 0 for a root category', VALUE_DEFAULT, 0), 'idnumber' => new external_value(PARAM_RAW, 'the new category idnumber', VALUE_OPTIONAL), 'description' => new external_value(PARAM_RAW, 'the new category description', VALUE_OPTIONAL), 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT), 'theme' => new external_value(PARAM_THEME, 'the new category theme. This option must be enabled on moodle', VALUE_OPTIONAL), ) ) ) ) ); } /** * Create categories * * @param array $categories - see create_categories_parameters() for the array structure * @return array - see create_categories_returns() for the array structure * @since Moodle 2.3 */ public static function create_categories($categories) { global $DB; $params = self::validate_parameters(self::create_categories_parameters(), array('categories' => $categories)); $transaction = $DB->start_delegated_transaction(); $createdcategories = array(); foreach ($params['categories'] as $category) { if ($category['parent']) { if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) { throw new moodle_exception('unknowcategory'); } $context = context_coursecat::instance($category['parent']); } else { $context = context_system::instance(); } self::validate_context($context); require_capability('moodle/category:manage', $context); // this will validate format and throw an exception if there are errors util::validate_format($category['descriptionformat']); $newcategory = core_course_category::create($category); $context = context_coursecat::instance($newcategory->id); $createdcategories[] = array( 'id' => $newcategory->id, 'name' => \core_external\util::format_string($newcategory->name, $context), ); } $transaction->allow_commit(); return $createdcategories; } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.3 */ public static function create_categories_returns() { return new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'new category id'), 'name' => new external_value(PARAM_RAW, 'new category name'), ) ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.3 */ public static function update_categories_parameters() { return new external_function_parameters( array( 'categories' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'course id'), 'name' => new external_value(PARAM_TEXT, 'category name', VALUE_OPTIONAL), 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL), 'parent' => new external_value(PARAM_INT, 'parent category id', VALUE_OPTIONAL), 'description' => new external_value(PARAM_RAW, 'category description', VALUE_OPTIONAL), 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT), 'theme' => new external_value(PARAM_THEME, 'the category theme. This option must be enabled on moodle', VALUE_OPTIONAL), ) ) ) ) ); } /** * Update categories * * @param array $categories The list of categories to update * @return null * @since Moodle 2.3 */ public static function update_categories($categories) { global $DB; // Validate parameters. $params = self::validate_parameters(self::update_categories_parameters(), array('categories' => $categories)); $transaction = $DB->start_delegated_transaction(); foreach ($params['categories'] as $cat) { $category = core_course_category::get($cat['id']); $categorycontext = context_coursecat::instance($cat['id']); self::validate_context($categorycontext); require_capability('moodle/category:manage', $categorycontext); // If the category parent is being changed, check for capability in the new parent category if (isset($cat['parent']) && ($cat['parent'] !== $category->parent)) { if ($cat['parent'] == 0) { // Creating a top level category requires capability in the system context $parentcontext = context_system::instance(); } else { // Category context $parentcontext = context_coursecat::instance($cat['parent']); } self::validate_context($parentcontext); require_capability('moodle/category:manage', $parentcontext); } // this will throw an exception if descriptionformat is not valid util::validate_format($cat['descriptionformat']); $category->update($cat); } $transaction->allow_commit(); } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.3 */ public static function update_categories_returns() { return null; } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.3 */ public static function delete_categories_parameters() { return new external_function_parameters( array( 'categories' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'category id to delete'), 'newparent' => new external_value(PARAM_INT, 'the parent category to move the contents to, if specified', VALUE_OPTIONAL), 'recursive' => new external_value(PARAM_BOOL, '1: recursively delete all contents inside this category, 0 (default): move contents to newparent or current parent category (except if parent is root)', VALUE_DEFAULT, 0) ) ) ) ) ); } /** * Delete categories * * @param array $categories A list of category ids * @return array * @since Moodle 2.3 */ public static function delete_categories($categories) { global $CFG, $DB; require_once($CFG->dirroot . "/course/lib.php"); // Validate parameters. $params = self::validate_parameters(self::delete_categories_parameters(), array('categories' => $categories)); $transaction = $DB->start_delegated_transaction(); foreach ($params['categories'] as $category) { $deletecat = core_course_category::get($category['id'], MUST_EXIST); $context = context_coursecat::instance($deletecat->id); require_capability('moodle/category:manage', $context); self::validate_context($context); self::validate_context(get_category_or_system_context($deletecat->parent)); if ($category['recursive']) { // If recursive was specified, then we recursively delete the category's contents. if ($deletecat->can_delete_full()) { $deletecat->delete_full(false); } else { throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name()); } } else { // In this situation, we don't delete the category's contents, we either move it to newparent or parent. // If the parent is the root, moving is not supported (because a course must always be inside a category). // We must move to an existing category. if (!empty($category['newparent'])) { $newparentcat = core_course_category::get($category['newparent']); } else { $newparentcat = core_course_category::get($deletecat->parent); } // This operation is not allowed. We must move contents to an existing category. if (!$newparentcat->id) { throw new moodle_exception('movecatcontentstoroot'); } self::validate_context(context_coursecat::instance($newparentcat->id)); if ($deletecat->can_move_content_to($newparentcat->id)) { $deletecat->delete_move($newparentcat->id, false); } else { throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name()); } } } $transaction->allow_commit(); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.3 */ public static function delete_categories_returns() { return null; } /** * Describes the parameters for delete_modules. * * @return external_function_parameters * @since Moodle 2.5 */ public static function delete_modules_parameters() { return new external_function_parameters ( array( 'cmids' => new external_multiple_structure(new external_value(PARAM_INT, 'course module ID', VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of course module IDs'), ) ); } /** * Deletes a list of provided module instances. * * @param array $cmids the course module ids * @since Moodle 2.5 */ public static function delete_modules($cmids) { global $CFG, $DB; // Require course file containing the course delete module function. require_once($CFG->dirroot . "/course/lib.php"); // Clean the parameters. $params = self::validate_parameters(self::delete_modules_parameters(), array('cmids' => $cmids)); // Keep track of the course ids we have performed a capability check on to avoid repeating. $arrcourseschecked = array(); foreach ($params['cmids'] as $cmid) { // Get the course module. $cm = $DB->get_record('course_modules', array('id' => $cmid), '*', MUST_EXIST); // Check if we have not yet confirmed they have permission in this course. if (!in_array($cm->course, $arrcourseschecked)) { // Ensure the current user has required permission in this course. $context = context_course::instance($cm->course); self::validate_context($context); // Add to the array. $arrcourseschecked[] = $cm->course; } // Ensure they can delete this module. $modcontext = context_module::instance($cm->id); require_capability('moodle/course:manageactivities', $modcontext); // Delete the module. course_delete_module($cm->id); } } /** * Describes the delete_modules return value. * * @return external_single_structure * @since Moodle 2.5 */ public static function delete_modules_returns() { return null; } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.9 */ public static function view_course_parameters() { return new external_function_parameters( array( 'courseid' => new external_value(PARAM_INT, 'id of the course'), 'sectionnumber' => new external_value(PARAM_INT, 'section number', VALUE_DEFAULT, 0) ) ); } /** * Trigger the course viewed event. * * @param int $courseid id of course * @param int $sectionnumber sectionnumber (0, 1, 2...) * @return array of warnings and status result * @since Moodle 2.9 * @throws moodle_exception */ public static function view_course($courseid, $sectionnumber = 0) { global $CFG; require_once($CFG->dirroot . "/course/lib.php"); $params = self::validate_parameters(self::view_course_parameters(), array( 'courseid' => $courseid, 'sectionnumber' => $sectionnumber )); $warnings = array(); $course = get_course($params['courseid']); $context = context_course::instance($course->id); self::validate_context($context); if (!empty($params['sectionnumber'])) { // Get section details and check it exists. $modinfo = get_fast_modinfo($course); $coursesection = $modinfo->get_section_info($params['sectionnumber'], MUST_EXIST); // Check user is allowed to see it. if (!$coursesection->uservisible) { require_capability('moodle/course:viewhiddensections', $context); } } course_view($context, $params['sectionnumber']); $result = array(); $result['status'] = true; $result['warnings'] = $warnings; return $result; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.9 */ public static function view_course_returns() { return new external_single_structure( array( 'status' => new external_value(PARAM_BOOL, 'status: true if success'), 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.0 */ public static function search_courses_parameters() { return new external_function_parameters( array( 'criterianame' => new external_value(PARAM_ALPHA, 'criteria name (search, modulelist (only admins), blocklist (only admins), tagid)'), 'criteriavalue' => new external_value(PARAM_RAW, 'criteria value'), 'page' => new external_value(PARAM_INT, 'page number (0 based)', VALUE_DEFAULT, 0), 'perpage' => new external_value(PARAM_INT, 'items per page', VALUE_DEFAULT, 0), 'requiredcapabilities' => new external_multiple_structure( new external_value(PARAM_CAPABILITY, 'Capability string used to filter courses by permission'), 'Optional list of required capabilities (used to filter the list)', VALUE_DEFAULT, array() ), 'limittoenrolled' => new external_value(PARAM_BOOL, 'limit to enrolled courses', VALUE_DEFAULT, 0), 'onlywithcompletion' => new external_value(PARAM_BOOL, 'limit to courses where completion is enabled', VALUE_DEFAULT, 0), ) ); } /** * Return the course information that is public (visible by every one) * * @param core_course_list_element $course course in list object * @param stdClass $coursecontext course context object * @return array the course information * @since Moodle 3.2 */ protected static function get_course_public_information(core_course_list_element $course, $coursecontext) { global $OUTPUT; static $categoriescache = array(); // Category information. if (!array_key_exists($course->category, $categoriescache)) { $categoriescache[$course->category] = core_course_category::get($course->category, IGNORE_MISSING); } $category = $categoriescache[$course->category]; // Retrieve course overview used files. $files = array(); foreach ($course->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); $files[] = array( 'filename' => $file->get_filename(), 'fileurl' => $fileurl, 'filesize' => $file->get_filesize(), 'filepath' => $file->get_filepath(), 'mimetype' => $file->get_mimetype(), 'timemodified' => $file->get_timemodified(), ); } // Retrieve the course contacts, // we need here the users fullname since if we are not enrolled can be difficult to obtain them via other Web Services. $coursecontacts = array(); foreach ($course->get_course_contacts() as $contact) { $coursecontacts[] = array( 'id' => $contact['user']->id, 'fullname' => $contact['username'], 'roles' => array_map(function($role){ return array('id' => $role->id, 'name' => $role->displayname); }, $contact['roles']), 'role' => array('id' => $contact['role']->id, 'name' => $contact['role']->displayname), 'rolename' => $contact['rolename'] ); } // Allowed enrolment methods (maybe we can self-enrol). $enroltypes = array(); $instances = enrol_get_instances($course->id, true); foreach ($instances as $instance) { $enroltypes[] = $instance->enrol; } // Format summary. list($summary, $summaryformat) = \core_external\util::format_text($course->summary, $course->summaryformat, $coursecontext, 'course', 'summary', null); $categoryname = ''; if (!empty($category)) { $categoryname = \core_external\util::format_string($category->name, $category->get_context()); } $displayname = get_course_display_name_for_list($course); $coursereturns = array(); $coursereturns['id'] = $course->id; $coursereturns['fullname'] = \core_external\util::format_string($course->fullname, $coursecontext); $coursereturns['displayname'] = \core_external\util::format_string($displayname, $coursecontext); $coursereturns['shortname'] = \core_external\util::format_string($course->shortname, $coursecontext); $coursereturns['categoryid'] = $course->category; $coursereturns['categoryname'] = $categoryname; $coursereturns['summary'] = $summary; $coursereturns['summaryformat'] = $summaryformat; $coursereturns['summaryfiles'] = util::get_area_files($coursecontext->id, 'course', 'summary', false, false); $coursereturns['overviewfiles'] = $files; $coursereturns['contacts'] = $coursecontacts; $coursereturns['enrollmentmethods'] = $enroltypes; $coursereturns['sortorder'] = $course->sortorder; $coursereturns['showactivitydates'] = $course->showactivitydates; $coursereturns['showcompletionconditions'] = $course->showcompletionconditions; $handler = core_course\customfield\course_handler::create(); if ($customfields = $handler->export_instance_data($course->id)) { $coursereturns['customfields'] = []; foreach ($customfields as $data) { $coursereturns['customfields'][] = [ 'type' => $data->get_type(), 'value' => $data->get_value(), 'valueraw' => $data->get_data_controller()->get_value(), 'name' => $data->get_name(), 'shortname' => $data->get_shortname() ]; } } $courseimage = \core_course\external\course_summary_exporter::get_course_image($course); if (!$courseimage) { $courseimage = $OUTPUT->get_generated_url_for_course($coursecontext); } $coursereturns['courseimage'] = $courseimage; return $coursereturns; } /** * Search courses following the specified criteria. * * @param string $criterianame Criteria name (search, modulelist (only admins), blocklist (only admins), tagid) * @param string $criteriavalue Criteria value * @param int $page Page number (for pagination) * @param int $perpage Items per page * @param array $requiredcapabilities Optional list of required capabilities (used to filter the list). * @param int $limittoenrolled Limit to only enrolled courses * @param int onlywithcompletion Limit to only courses where completion is enabled * @return array of course objects and warnings * @since Moodle 3.0 * @throws moodle_exception */ public static function search_courses($criterianame, $criteriavalue, $page=0, $perpage=0, $requiredcapabilities=array(), $limittoenrolled=0, $onlywithcompletion=0) { global $CFG; $warnings = array(); $parameters = array( 'criterianame' => $criterianame, 'criteriavalue' => $criteriavalue, 'page' => $page, 'perpage' => $perpage, 'requiredcapabilities' => $requiredcapabilities, 'limittoenrolled' => $limittoenrolled, 'onlywithcompletion' => $onlywithcompletion ); $params = self::validate_parameters(self::search_courses_parameters(), $parameters); self::validate_context(context_system::instance()); $allowedcriterianames = array('search', 'modulelist', 'blocklist', 'tagid'); if (!in_array($params['criterianame'], $allowedcriterianames)) { throw new invalid_parameter_exception('Invalid value for criterianame parameter (value: '.$params['criterianame'].'),' . 'allowed values are: '.implode(',', $allowedcriterianames)); } if ($params['criterianame'] == 'modulelist' or $params['criterianame'] == 'blocklist') { require_capability('moodle/site:config', context_system::instance()); } $paramtype = array( 'search' => PARAM_RAW, 'modulelist' => PARAM_PLUGIN, 'blocklist' => PARAM_INT, 'tagid' => PARAM_INT ); $params['criteriavalue'] = clean_param($params['criteriavalue'], $paramtype[$params['criterianame']]); // Prepare the search API options. $searchcriteria = array(); $searchcriteria[$params['criterianame']] = $params['criteriavalue']; if ($params['onlywithcompletion']) { $searchcriteria['onlywithcompletion'] = true; } if ($params['limittoenrolled']) { $searchcriteria['limittoenrolled'] = true; } $options = array(); if ($params['perpage'] != 0) { $offset = $params['page'] * $params['perpage']; $options = array('offset' => $offset, 'limit' => $params['perpage']); } // Search the courses. $courses = core_course_category::search_courses($searchcriteria, $options, $params['requiredcapabilities']); $totalcount = core_course_category::search_courses_count($searchcriteria, $options, $params['requiredcapabilities']); $finalcourses = array(); $categoriescache = array(); foreach ($courses as $course) { $coursecontext = context_course::instance($course->id); $finalcourses[] = self::get_course_public_information($course, $coursecontext); } return array( 'total' => $totalcount, 'courses' => $finalcourses, 'warnings' => $warnings ); } /** * Returns a course structure definition * * @param boolean $onlypublicdata set to true, to retrieve only fields viewable by anyone when the course is visible * @return external_single_structure the course structure * @since Moodle 3.2 */ protected static function get_course_structure($onlypublicdata = true) { $coursestructure = array( 'id' => new external_value(PARAM_INT, 'course id'), 'fullname' => new external_value(PARAM_RAW, 'course full name'), 'displayname' => new external_value(PARAM_RAW, 'course display name'), 'shortname' => new external_value(PARAM_RAW, 'course short name'), 'courseimage' => new external_value(PARAM_URL, 'Course image', VALUE_OPTIONAL), 'categoryid' => new external_value(PARAM_INT, 'category id'), 'categoryname' => new external_value(PARAM_RAW, 'category name'), 'sortorder' => new external_value(PARAM_INT, 'Sort order in the category', VALUE_OPTIONAL), 'summary' => new external_value(PARAM_RAW, 'summary'), 'summaryformat' => new external_format_value('summary'), 'summaryfiles' => new external_files('summary files in the summary field', VALUE_OPTIONAL), 'overviewfiles' => new external_files('additional overview files attached to this course'), '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'), 'contacts' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'contact user id'), 'fullname' => new external_value(PARAM_NOTAGS, 'contact user fullname'), ) ), 'contact users' ), 'enrollmentmethods' => new external_multiple_structure( new external_value(PARAM_PLUGIN, 'enrollment method'), 'enrollment methods list' ), 'customfields' => new external_multiple_structure( new external_single_structure( array( 'name' => new external_value(PARAM_RAW, 'The name of the custom field'), 'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field - to be able to build the field class in the code'), 'type' => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field - text field, checkbox...'), 'valueraw' => new external_value(PARAM_RAW, 'The raw value of the custom field'), 'value' => new external_value(PARAM_RAW, 'The value of the custom field'), ) ), 'Custom fields', VALUE_OPTIONAL), ); if (!$onlypublicdata) { $extra = array( 'idnumber' => new external_value(PARAM_RAW, 'Id number', VALUE_OPTIONAL), 'format' => new external_value(PARAM_PLUGIN, 'Course format: weeks, topics, social, site,..', VALUE_OPTIONAL), 'showgrades' => new external_value(PARAM_INT, '1 if grades are shown, otherwise 0', VALUE_OPTIONAL), 'newsitems' => new external_value(PARAM_INT, 'Number of recent items appearing on the course page', 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), 'maxbytes' => new external_value(PARAM_INT, 'Largest size of file that can be uploaded into', VALUE_OPTIONAL), 'showreports' => new external_value(PARAM_INT, 'Are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL), 'visible' => new external_value(PARAM_INT, '1: available to student, 0:not available', VALUE_OPTIONAL), 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL), 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL), 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL), 'enablecompletion' => new external_value(PARAM_INT, 'Completion enabled? 1: yes 0: no', VALUE_OPTIONAL), 'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL), 'lang' => new external_value(PARAM_SAFEDIR, 'Forced course language', VALUE_OPTIONAL), 'theme' => new external_value(PARAM_PLUGIN, 'Fame of the forced theme', VALUE_OPTIONAL), 'marker' => new external_value(PARAM_INT, 'Current course marker', VALUE_OPTIONAL), 'legacyfiles' => new external_value(PARAM_INT, 'If legacy files are enabled', VALUE_OPTIONAL), 'calendartype' => new external_value(PARAM_PLUGIN, 'Calendar type', VALUE_OPTIONAL), 'timecreated' => new external_value(PARAM_INT, 'Time when the course was created', VALUE_OPTIONAL), 'timemodified' => new external_value(PARAM_INT, 'Last time the course was updated', VALUE_OPTIONAL), 'requested' => new external_value(PARAM_INT, 'If is a requested course', VALUE_OPTIONAL), 'cacherev' => new external_value(PARAM_INT, 'Cache revision number', VALUE_OPTIONAL), 'filters' => new external_multiple_structure( new external_single_structure( array( 'filter' => new external_value(PARAM_PLUGIN, 'Filter plugin name'), 'localstate' => new external_value(PARAM_INT, 'Filter state: 1 for on, -1 for off, 0 if inherit'), 'inheritedstate' => new external_value(PARAM_INT, '1 or 0 to use when localstate is set to inherit'), ) ), 'Course filters', VALUE_OPTIONAL ), 'courseformatoptions' => new external_multiple_structure( new external_single_structure( array( 'name' => new external_value(PARAM_RAW, 'Course format option name.'), 'value' => new external_value(PARAM_RAW, 'Course format option value.'), ) ), 'Additional options for particular course format.', VALUE_OPTIONAL ), 'communicationroomname' => new external_value(PARAM_TEXT, 'Communication tool room name.', VALUE_OPTIONAL), 'communicationroomurl' => new external_value(PARAM_RAW, 'Communication tool room URL.', VALUE_OPTIONAL), ); $coursestructure = array_merge($coursestructure, $extra); } return new external_single_structure($coursestructure); } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 3.0 */ public static function search_courses_returns() { return new external_single_structure( array( 'total' => new external_value(PARAM_INT, 'total course count'), 'courses' => new external_multiple_structure(self::get_course_structure(), 'course'), 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.0 */ public static function get_course_module_parameters() { return new external_function_parameters( array( 'cmid' => new external_value(PARAM_INT, 'The course module id') ) ); } /** * Return information about a course module. * * @param int $cmid the course module id * @return array of warnings and the course module * @since Moodle 3.0 * @throws moodle_exception */ public static function get_course_module($cmid) { global $CFG, $DB; $params = self::validate_parameters(self::get_course_module_parameters(), array('cmid' => $cmid)); $warnings = array(); $cm = get_coursemodule_from_id(null, $params['cmid'], 0, true, MUST_EXIST); $context = context_module::instance($cm->id); self::validate_context($context); // If the user has permissions to manage the activity, return all the information. if (has_capability('moodle/course:manageactivities', $context)) { require_once($CFG->dirroot . '/course/modlib.php'); require_once($CFG->libdir . '/gradelib.php'); $info = $cm; // Get the extra information: grade, advanced grading and outcomes data. $course = get_course($cm->course); list($newcm, $newcontext, $module, $extrainfo, $cw) = get_moduleinfo_data($cm, $course); // Grades. $gradeinfo = array('grade', 'gradepass', 'gradecat'); foreach ($gradeinfo as $gfield) { if (isset($extrainfo->{$gfield})) { $info->{$gfield} = $extrainfo->{$gfield}; } } if (isset($extrainfo->grade) and $extrainfo->grade < 0) { $info->scale = $DB->get_field('scale', 'scale', array('id' => abs($extrainfo->grade))); } // Advanced grading. if (isset($extrainfo->_advancedgradingdata)) { $info->advancedgrading = array(); foreach ($extrainfo as $key => $val) { if (strpos($key, 'advancedgradingmethod_') === 0) { $info->advancedgrading[] = array( 'area' => str_replace('advancedgradingmethod_', '', $key), 'method' => $val ); } } } // Outcomes. foreach ($extrainfo as $key => $val) { if (strpos($key, 'outcome_') === 0) { if (!isset($info->outcomes)) { $info->outcomes = array(); } $id = str_replace('outcome_', '', $key); $outcome = grade_outcome::fetch(array('id' => $id)); $scaleitems = $outcome->load_scale(); $info->outcomes[] = array( 'id' => $id, 'name' => \core_external\util::format_string($outcome->get_name(), $context), 'scale' => $scaleitems->scale ); } } } else { // Return information is safe to show to any user. $info = new stdClass(); $info->id = $cm->id; $info->course = $cm->course; $info->module = $cm->module; $info->modname = $cm->modname; $info->instance = $cm->instance; $info->section = $cm->section; $info->sectionnum = $cm->sectionnum; $info->groupmode = $cm->groupmode; $info->groupingid = $cm->groupingid; $info->completion = $cm->completion; $info->downloadcontent = $cm->downloadcontent; } // Format name. $info->name = \core_external\util::format_string($cm->name, $context); $result = array(); $result['cm'] = $info; $result['warnings'] = $warnings; return $result; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 3.0 */ public static function get_course_module_returns() { return new external_single_structure( array( 'cm' => new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'The course module id'), 'course' => new external_value(PARAM_INT, 'The course id'), 'module' => new external_value(PARAM_INT, 'The module type id'), 'name' => new external_value(PARAM_RAW, 'The activity name'), 'modname' => new external_value(PARAM_COMPONENT, 'The module component name (forum, assign, etc..)'), 'instance' => new external_value(PARAM_INT, 'The activity instance id'), 'section' => new external_value(PARAM_INT, 'The module section id'), 'sectionnum' => new external_value(PARAM_INT, 'The module section number'), 'groupmode' => new external_value(PARAM_INT, 'Group mode'), 'groupingid' => new external_value(PARAM_INT, 'Grouping id'), 'completion' => new external_value(PARAM_INT, 'If completion is enabled'), 'idnumber' => new external_value(PARAM_RAW, 'Module id number', VALUE_OPTIONAL), 'added' => new external_value(PARAM_INT, 'Time added', VALUE_OPTIONAL), 'score' => new external_value(PARAM_INT, 'Score', VALUE_OPTIONAL), 'indent' => new external_value(PARAM_INT, 'Indentation', VALUE_OPTIONAL), 'visible' => new external_value(PARAM_INT, 'If visible', VALUE_OPTIONAL), 'visibleoncoursepage' => new external_value(PARAM_INT, 'If visible on course page', VALUE_OPTIONAL), 'visibleold' => new external_value(PARAM_INT, 'Visible old', VALUE_OPTIONAL), 'completiongradeitemnumber' => new external_value(PARAM_INT, 'Completion grade item', VALUE_OPTIONAL), 'completionpassgrade' => new external_value(PARAM_INT, 'Completion pass grade setting', VALUE_OPTIONAL), 'completionview' => new external_value(PARAM_INT, 'Completion view setting', VALUE_OPTIONAL), 'completionexpected' => new external_value(PARAM_INT, 'Completion time expected', VALUE_OPTIONAL), 'showdescription' => new external_value(PARAM_INT, 'If the description is showed', VALUE_OPTIONAL), 'downloadcontent' => new external_value(PARAM_INT, 'The download content value', VALUE_OPTIONAL), 'availability' => new external_value(PARAM_RAW, 'Availability settings', VALUE_OPTIONAL), 'grade' => new external_value(PARAM_FLOAT, 'Grade (max value or scale id)', VALUE_OPTIONAL), 'scale' => new external_value(PARAM_TEXT, 'Scale items (if used)', VALUE_OPTIONAL), 'gradepass' => new external_value(PARAM_RAW, 'Grade to pass (float)', VALUE_OPTIONAL), 'gradecat' => new external_value(PARAM_INT, 'Grade category', VALUE_OPTIONAL), 'advancedgrading' => new external_multiple_structure( new external_single_structure( array( 'area' => new external_value(PARAM_AREA, 'Gradable area name'), 'method' => new external_value(PARAM_COMPONENT, 'Grading method'), ) ), 'Advanced grading settings', VALUE_OPTIONAL ), 'outcomes' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_ALPHANUMEXT, 'Outcome id'), 'name' => new external_value(PARAM_RAW, 'Outcome full name'), 'scale' => new external_value(PARAM_TEXT, 'Scale items') ) ), 'Outcomes information', VALUE_OPTIONAL ), ) ), 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.0 */ public static function get_course_module_by_instance_parameters() { return new external_function_parameters( array( 'module' => new external_value(PARAM_COMPONENT, 'The module name'), 'instance' => new external_value(PARAM_INT, 'The module instance id') ) ); } /** * Return information about a course module. * * @param string $module the module name * @param int $instance the activity instance id * @return array of warnings and the course module * @since Moodle 3.0 * @throws moodle_exception */ public static function get_course_module_by_instance($module, $instance) { $params = self::validate_parameters(self::get_course_module_by_instance_parameters(), array( 'module' => $module, 'instance' => $instance, )); $warnings = array(); $cm = get_coursemodule_from_instance($params['module'], $params['instance'], 0, false, MUST_EXIST); return self::get_course_module($cm->id); } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 3.0 */ public static function get_course_module_by_instance_returns() { return self::get_course_module_returns(); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.2 */ public static function get_user_navigation_options_parameters() { return new external_function_parameters( array( 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'Course id.')), ) ); } /** * Return a list of navigation options in a set of courses that are avaialable or not for the current user. * * @param array $courseids a list of course ids * @return array of warnings and the options availability * @since Moodle 3.2 * @throws moodle_exception */ public static function get_user_navigation_options($courseids) { global $CFG; require_once($CFG->dirroot . '/course/lib.php'); // Parameter validation. $params = self::validate_parameters(self::get_user_navigation_options_parameters(), array('courseids' => $courseids)); $courseoptions = array(); list($courses, $warnings) = util::validate_courses($params['courseids'], array(), true); if (!empty($courses)) { foreach ($courses as $course) { // Fix the context for the frontpage. if ($course->id == SITEID) { $course->context = context_system::instance(); } $navoptions = course_get_user_navigation_options($course->context, $course); $options = array(); foreach ($navoptions as $name => $available) { $options[] = array( 'name' => $name, 'available' => $available, ); } $courseoptions[] = array( 'id' => $course->id, 'options' => $options ); } } $result = array( 'courses' => $courseoptions, 'warnings' => $warnings ); return $result; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 3.2 */ public static function get_user_navigation_options_returns() { return new external_single_structure( array( 'courses' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'Course id'), 'options' => new external_multiple_structure( new external_single_structure( array( 'name' => new external_value(PARAM_ALPHANUMEXT, 'Option name'), 'available' => new external_value(PARAM_BOOL, 'Whether the option is available or not'), ) ) ) ) ), 'List of courses' ), 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.2 */ public static function get_user_administration_options_parameters() { return new external_function_parameters( array( 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'Course id.')), ) ); } /** * Return a list of administration options in a set of courses that are available or not for the current user. * * @param array $courseids a list of course ids * @return array of warnings and the options availability * @since Moodle 3.2 * @throws moodle_exception */ public static function get_user_administration_options($courseids) { global $CFG; require_once($CFG->dirroot . '/course/lib.php'); // Parameter validation. $params = self::validate_parameters(self::get_user_administration_options_parameters(), array('courseids' => $courseids)); $courseoptions = array(); list($courses, $warnings) = util::validate_courses($params['courseids'], array(), true); if (!empty($courses)) { foreach ($courses as $course) { $adminoptions = course_get_user_administration_options($course, $course->context); $options = array(); foreach ($adminoptions as $name => $available) { $options[] = array( 'name' => $name, 'available' => $available, ); } $courseoptions[] = array( 'id' => $course->id, 'options' => $options ); } } $result = array( 'courses' => $courseoptions, 'warnings' => $warnings ); return $result; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 3.2 */ public static function get_user_administration_options_returns() { return self::get_user_navigation_options_returns(); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.2 */ public static function get_courses_by_field_parameters() { return new external_function_parameters( array( 'field' => new external_value(PARAM_ALPHA, 'The field to search can be left empty for all courses or: id: course id ids: comma separated course ids shortname: course short name idnumber: course id number category: category id the course belongs to sectionid: section id that belongs to a course ', VALUE_DEFAULT, ''), 'value' => new external_value(PARAM_RAW, 'The value to match', VALUE_DEFAULT, '') ) ); } /** * Get courses matching a specific field (id/s, shortname, idnumber, category) * * @param string $field field name to search, or empty for all courses * @param string $value value to search * @return array list of courses and warnings * @throws invalid_parameter_exception * @since Moodle 3.2 */ public static function get_courses_by_field($field = '', $value = '') { global $DB, $CFG; require_once($CFG->dirroot . '/course/lib.php'); require_once($CFG->libdir . '/filterlib.php'); $params = self::validate_parameters(self::get_courses_by_field_parameters(), array( 'field' => $field, 'value' => $value, ) ); $warnings = array(); if (empty($params['field'])) { $courses = $DB->get_records('course', null, 'id ASC'); } else { switch ($params['field']) { case 'id': case 'category': case 'sectionid': $value = clean_param($params['value'], PARAM_INT); break; case 'ids': $value = clean_param($params['value'], PARAM_SEQUENCE); break; case 'shortname': $value = clean_param($params['value'], PARAM_TEXT); break; case 'idnumber': $value = clean_param($params['value'], PARAM_RAW); break; default: throw new invalid_parameter_exception('Invalid field name'); } if ($params['field'] === 'ids') { // Preload categories to avoid loading one at a time. $courseids = explode(',', $value); list ($listsql, $listparams) = $DB->get_in_or_equal($courseids); $categoryids = $DB->get_fieldset_sql(" SELECT DISTINCT cc.id FROM {course} c JOIN {course_categories} cc ON cc.id = c.category WHERE c.id $listsql", $listparams); core_course_category::get_many($categoryids); // Load and validate all courses. This is called because it loads the courses // more efficiently. list ($courses, $warnings) = util::validate_courses($courseids, [], false, true); } else if ($params['field'] === 'sectionid') { $courseid = $DB->get_field('course_sections', 'course', ['id' => $value]); $courses = $courseid ? [$DB->get_record('course', ['id' => $courseid])] : []; } else { $courses = $DB->get_records('course', array($params['field'] => $value), 'id ASC'); } } $iscommapiavailable = \core_communication\api::is_available(); $coursesdata = array(); foreach ($courses as $course) { $context = context_course::instance($course->id); $canupdatecourse = has_capability('moodle/course:update', $context); $canviewhiddencourses = has_capability('moodle/course:viewhiddencourses', $context); // Check if the course is visible in the site for the user. if (!$course->visible and !$canviewhiddencourses and !$canupdatecourse) { continue; } // Get the public course information, even if we are not enrolled. $courseinlist = new core_course_list_element($course); // Now, check if we have access to the course, unless it was already checked. try { if (empty($course->contextvalidated)) { self::validate_context($context); } } catch (Exception $e) { // User can not access the course, check if they can see the public information about the course and return it. if (core_course_category::can_view_course_info($course)) { $coursesdata[$course->id] = self::get_course_public_information($courseinlist, $context); } continue; } $coursesdata[$course->id] = self::get_course_public_information($courseinlist, $context); // Return information for any user that can access the course. $coursefields = array('format', 'showgrades', 'newsitems', 'startdate', 'enddate', 'maxbytes', 'showreports', 'visible', 'groupmode', 'groupmodeforce', 'defaultgroupingid', 'enablecompletion', 'completionnotify', 'lang', 'theme', 'marker'); // Course filters. $coursesdata[$course->id]['filters'] = filter_get_available_in_context($context); // Information for managers only. if ($canupdatecourse) { $managerfields = array('idnumber', 'legacyfiles', 'calendartype', 'timecreated', 'timemodified', 'requested', 'cacherev'); $coursefields = array_merge($coursefields, $managerfields); } // Populate fields. foreach ($coursefields as $field) { $coursesdata[$course->id][$field] = $course->{$field}; } // Clean lang and auth fields for external functions (it may content uninstalled themes or language packs). if (isset($coursesdata[$course->id]['theme'])) { $coursesdata[$course->id]['theme'] = clean_param($coursesdata[$course->id]['theme'], PARAM_THEME); } if (isset($coursesdata[$course->id]['lang'])) { $coursesdata[$course->id]['lang'] = clean_param($coursesdata[$course->id]['lang'], PARAM_LANG); } $courseformatoptions = course_get_format($course)->get_config_for_external(); foreach ($courseformatoptions as $key => $value) { $coursesdata[$course->id]['courseformatoptions'][] = array( 'name' => $key, 'value' => $value ); } // Communication tools for the course. if ($iscommapiavailable) { $communication = \core_communication\api::load_by_instance( context: $context, component: 'core_course', instancetype: 'coursecommunication', instanceid: $course->id ); if ($communication->get_provider()) { $coursesdata[$course->id]['communicationroomname'] = \core_external\util::format_string($communication->get_room_name(), $context); // This will be usually an URL, however, it is better to consider that can be anything a plugin might return, this is why we will use PARAM_RAW. $coursesdata[$course->id]['communicationroomurl'] = $communication->get_communication_room_url(); } } } return array( 'courses' => $coursesdata, 'warnings' => $warnings ); } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 3.2 */ public static function get_courses_by_field_returns() { // Course structure, including not only public viewable fields. return new external_single_structure( array( 'courses' => new external_multiple_structure(self::get_course_structure(false), 'Course'), 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.2 */ public static function check_updates_parameters() { return new external_function_parameters( array( 'courseid' => new external_value(PARAM_INT, 'Course id to check'), 'tocheck' => new external_multiple_structure( new external_single_structure( array( 'contextlevel' => new external_value(PARAM_ALPHA, 'The context level for the file location. Only module supported right now.'), 'id' => new external_value(PARAM_INT, 'Context instance id'), 'since' => new external_value(PARAM_INT, 'Check updates since this time stamp'), ) ), 'Instances to check' ), 'filter' => new external_multiple_structure( new external_value(PARAM_ALPHANUM, 'Area name: configuration, fileareas, completion, ratings, comments, gradeitems, outcomes'), 'Check only for updates in these areas', VALUE_DEFAULT, array() ) ) ); } /** * Check if there is updates affecting the user for the given course and contexts. * Right now only modules are supported. * This WS calls mod_check_updates_since for each module to check if there is any update the user should we aware of. * * @param int $courseid the list of modules to check * @param array $tocheck the list of modules to check * @param array $filter check only for updates in these areas * @return array list of updates and warnings * @throws moodle_exception * @since Moodle 3.2 */ public static function check_updates($courseid, $tocheck, $filter = array()) { global $CFG, $DB; require_once($CFG->dirroot . "/course/lib.php"); $params = self::validate_parameters( self::check_updates_parameters(), array( 'courseid' => $courseid, 'tocheck' => $tocheck, 'filter' => $filter, ) ); $course = get_course($params['courseid']); $context = context_course::instance($course->id); self::validate_context($context); list($instances, $warnings) = course_check_updates($course, $params['tocheck'], $filter); $instancesformatted = array(); foreach ($instances as $instance) { $updates = array(); foreach ($instance['updates'] as $name => $data) { if (empty($data->updated)) { continue; } $updatedata = array( 'name' => $name, ); if (!empty($data->timeupdated)) { $updatedata['timeupdated'] = $data->timeupdated; } if (!empty($data->itemids)) { $updatedata['itemids'] = $data->itemids; } $updates[] = $updatedata; } if (!empty($updates)) { $instancesformatted[] = array( 'contextlevel' => $instance['contextlevel'], 'id' => $instance['id'], 'updates' => $updates ); } } return array( 'instances' => $instancesformatted, 'warnings' => $warnings ); } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 3.2 */ public static function check_updates_returns() { return new external_single_structure( array( 'instances' => new external_multiple_structure( new external_single_structure( array( 'contextlevel' => new external_value(PARAM_ALPHA, 'The context level'), 'id' => new external_value(PARAM_INT, 'Instance id'), 'updates' => new external_multiple_structure( new external_single_structure( array( 'name' => new external_value(PARAM_ALPHANUMEXT, 'Name of the area updated.'), 'timeupdated' => new external_value(PARAM_INT, 'Last time was updated', VALUE_OPTIONAL), 'itemids' => new external_multiple_structure( new external_value(PARAM_INT, 'Instance id'), 'The ids of the items updated', VALUE_OPTIONAL ) ) ) ) ) ) ), 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.3 */ public static function get_updates_since_parameters() { return new external_function_parameters( array( 'courseid' => new external_value(PARAM_INT, 'Course id to check'), 'since' => new external_value(PARAM_INT, 'Check updates since this time stamp'), 'filter' => new external_multiple_structure( new external_value(PARAM_ALPHANUM, 'Area name: configuration, fileareas, completion, ratings, comments, gradeitems, outcomes'), 'Check only for updates in these areas', VALUE_DEFAULT, array() ) ) ); } /** * Check if there are updates affecting the user for the given course since the given time stamp. * * This function is a wrapper of self::check_updates for retrieving all the updates since a given time for all the activities. * * @param int $courseid the list of modules to check * @param int $since check updates since this time stamp * @param array $filter check only for updates in these areas * @return array list of updates and warnings * @throws moodle_exception * @since Moodle 3.3 */ public static function get_updates_since($courseid, $since, $filter = array()) { global $CFG, $DB; $params = self::validate_parameters( self::get_updates_since_parameters(), array( 'courseid' => $courseid, 'since' => $since, 'filter' => $filter, ) ); $course = get_course($params['courseid']); $modinfo = get_fast_modinfo($course); $tocheck = array(); // Retrieve all the visible course modules for the current user. $cms = $modinfo->get_cms(); foreach ($cms as $cm) { if (!$cm->uservisible) { continue; } $tocheck[] = array( 'id' => $cm->id, 'contextlevel' => 'module', 'since' => $params['since'], ); } return self::check_updates($course->id, $tocheck, $params['filter']); } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 3.3 */ public static function get_updates_since_returns() { return self::check_updates_returns(); } /** * Mark the edit_module as deprecated. * @todo Remove this method in Moodle 6.0 (MDL-83530). * @return bool */ public static function edit_module_is_deprecated() { return true; } /** * Parameters for function edit_module() * * @deprecated since 5.0 * @todo Remove this method in Moodle 6.0 (MDL-83530). * @return external_function_parameters */ public static function edit_module_parameters() { return new external_function_parameters( array( 'action' => new external_value(PARAM_ALPHA, 'action: hide, show, stealth, duplicate, delete, moveleft, moveright, group...', VALUE_REQUIRED), 'id' => new external_value(PARAM_INT, 'course module id', VALUE_REQUIRED), 'sectionreturn' => new external_value(PARAM_INT, 'section to return to', VALUE_DEFAULT, null), )); } /** * Performs one of the edit module actions and return new html for AJAX * * Returns html to replace the current module html with, for example: * - empty string for "delete" action, * - two modules html for "duplicate" action * - updated module html for everything else * * Throws exception if operation is not permitted/possible * * @deprecated since 5.0 * @todo Remove this method in Moodle 6.0 (MDL-83530). * @param string $action * @param int $id * @param null|int $sectionreturn * @return string */ public static function edit_module($action, $id, $sectionreturn = null) { global $PAGE, $DB; // Validate and normalize parameters. $params = self::validate_parameters(self::edit_module_parameters(), array('action' => $action, 'id' => $id, 'sectionreturn' => $sectionreturn)); $action = $params['action']; $id = $params['id']; $sectionreturn = $params['sectionreturn']; // Set of permissions an editing user may have. $contextarray = [ 'moodle/course:update', 'moodle/course:manageactivities', 'moodle/course:activityvisibility', 'moodle/course:sectionvisibility', 'moodle/course:movesections', 'moodle/course:setcurrentsection', ]; $PAGE->set_other_editing_capability($contextarray); list($course, $cm) = get_course_and_cm_from_cmid($id); $modcontext = context_module::instance($cm->id); $coursecontext = context_course::instance($course->id); self::validate_context($modcontext); $format = course_get_format($course); if (!is_null($sectionreturn)) { $format->set_sectionnum($sectionreturn); } $renderer = $format->get_renderer($PAGE); switch($action) { case 'hide': case 'show': case 'stealth': require_capability('moodle/course:activityvisibility', $modcontext); $visible = ($action === 'hide') ? 0 : 1; $visibleoncoursepage = ($action === 'stealth') ? 0 : 1; set_coursemodule_visible($id, $visible, $visibleoncoursepage); \core\event\course_module_updated::create_from_cm($cm, $modcontext)->trigger(); break; case 'duplicate': require_capability('moodle/course:manageactivities', $coursecontext); require_capability('moodle/backup:backuptargetimport', $coursecontext); require_capability('moodle/restore:restoretargetimport', $coursecontext); if (!course_allowed_module($course, $cm->modname)) { throw new moodle_exception('No permission to create that activity'); } if ($newcm = duplicate_module($course, $cm)) { $modinfo = $format->get_modinfo(); $section = $modinfo->get_section_info($newcm->sectionnum); $cm = $modinfo->get_cm($id); // Get both original and new element html. $result = $renderer->course_section_updated_cm_item($format, $section, $cm); $result .= $renderer->course_section_updated_cm_item($format, $section, $newcm); return $result; } break; case 'groupsseparate': case 'groupsvisible': case 'groupsnone': require_capability('moodle/course:manageactivities', $modcontext); if ($action === 'groupsseparate') { $newgroupmode = SEPARATEGROUPS; } else if ($action === 'groupsvisible') { $newgroupmode = VISIBLEGROUPS; } else { $newgroupmode = NOGROUPS; } if (set_coursemodule_groupmode($cm->id, $newgroupmode)) { \core\event\course_module_updated::create_from_cm($cm, $modcontext)->trigger(); } break; case 'moveleft': case 'moveright': require_capability('moodle/course:manageactivities', $modcontext); $indent = $cm->indent + (($action === 'moveright') ? 1 : -1); if ($cm->indent >= 0) { $DB->update_record('course_modules', array('id' => $cm->id, 'indent' => $indent)); rebuild_course_cache($cm->course); } break; case 'delete': require_capability('moodle/course:manageactivities', $modcontext); course_delete_module($cm->id, true); return ''; default: throw new coding_exception('Unrecognised action'); } $modinfo = $format->get_modinfo(); $section = $modinfo->get_section_info($cm->sectionnum); $cm = $modinfo->get_cm($id); return $renderer->course_section_updated_cm_item($format, $section, $cm); } /** * Return structure for edit_module() * * @deprecated since 5.0 * @todo Remove this method in Moodle 6.0 (MDL-83530). * @return \core_external\external_description */ public static function edit_module_returns() { return new external_value(PARAM_RAW, 'html to replace the current module with'); } /** * Mark the get_module as deprecated. * @todo Remove this method in Moodle 6.0 (MDL-83530). * @return bool */ public static function get_module_is_deprecated() { return true; } /** * Parameters for function get_module() * * @deprecated since 5.0 * @todo Remove this method in Moodle 6.0 (MDL-83530). * @return external_function_parameters */ public static function get_module_parameters() { return new external_function_parameters( array( 'id' => new external_value(PARAM_INT, 'course module id', VALUE_REQUIRED), 'sectionreturn' => new external_value(PARAM_INT, 'section to return to', VALUE_DEFAULT, null), )); } /** * Returns html for displaying one activity module on course page * * @deprecated since 5.0 * @todo Remove this method in Moodle 6.0 (MDL-83530). * @param int $id * @param null|int $sectionreturn * @return string */ public static function get_module($id, $sectionreturn = null) { global $PAGE; // Validate and normalize parameters. $params = self::validate_parameters(self::get_module_parameters(), array('id' => $id, 'sectionreturn' => $sectionreturn)); $id = $params['id']; $sectionreturn = $params['sectionreturn']; // Set of permissions an editing user may have. $contextarray = [ 'moodle/course:update', 'moodle/course:manageactivities', 'moodle/course:activityvisibility', 'moodle/course:sectionvisibility', 'moodle/course:movesections', 'moodle/course:setcurrentsection', ]; $PAGE->set_other_editing_capability($contextarray); // Validate access to the course (note, this is html for the course view page, we don't validate access to the module). list($course, $cm) = get_course_and_cm_from_cmid($id); self::validate_context(context_course::instance($course->id)); $format = course_get_format($course); if (!is_null($sectionreturn)) { $format->set_sectionnum($sectionreturn); } $renderer = $format->get_renderer($PAGE); $modinfo = $format->get_modinfo(); $section = $modinfo->get_section_info($cm->sectionnum); return $renderer->course_section_updated_cm_item($format, $section, $cm); } /** * Return structure for get_module() * * @deprecated since 5.0 * @todo Remove this method in Moodle 6.0 (MDL-83530). * @return \core_external\external_description */ public static function get_module_returns() { return new external_value(PARAM_RAW, 'html to replace the current module with'); } /** * Mark the edit_section as deprecated. * @todo Remove this method in Moodle 6.0 (MDL-83530). * @return bool */ public static function edit_section_is_deprecated() { return true; } /** * Parameters for function edit_section() * * @deprecated since 5.0 * @todo Remove this method in Moodle 6.0 (MDL-83530). * @return external_function_parameters */ public static function edit_section_parameters() { return new external_function_parameters( array( 'action' => new external_value(PARAM_ALPHA, 'action: hide, show, stealth, setmarker, removemarker', VALUE_REQUIRED), 'id' => new external_value(PARAM_INT, 'course section id', VALUE_REQUIRED), 'sectionreturn' => new external_value(PARAM_INT, 'section to return to', VALUE_DEFAULT, null), )); } /** * Performs one of the edit section actions * * @deprecated since 5.0 * @todo Remove this method in Moodle 6.0 (MDL-83530). * @param string $action * @param int $id section id * @param int $sectionreturn section to return to * @return string */ public static function edit_section($action, $id, $sectionreturn) { global $DB; // Validate and normalize parameters. $params = self::validate_parameters(self::edit_section_parameters(), array('action' => $action, 'id' => $id, 'sectionreturn' => $sectionreturn)); $action = $params['action']; $id = $params['id']; $sr = $params['sectionreturn']; $section = $DB->get_record('course_sections', array('id' => $id), '*', MUST_EXIST); $coursecontext = context_course::instance($section->course); self::validate_context($coursecontext); $rv = course_get_format($section->course)->section_action($section, $action, $sectionreturn); if ($rv) { return json_encode($rv); } else { return null; } } /** * Return structure for edit_section() * * @deprecated since 5.0 * @todo Remove this method in Moodle 6.0 (MDL-83530). * @return \core_external\external_description */ public static function edit_section_returns() { return new external_value(PARAM_RAW, 'Additional data for javascript (JSON-encoded string)'); } /** * Returns description of method parameters * * @return external_function_parameters */ public static function get_enrolled_courses_by_timeline_classification_parameters() { return new external_function_parameters( array( 'classification' => new external_value(PARAM_ALPHA, 'future, inprogress, or past'), 'limit' => new external_value(PARAM_INT, 'Result set limit', VALUE_DEFAULT, 0), 'offset' => new external_value(PARAM_INT, 'Result set offset', VALUE_DEFAULT, 0), 'sort' => new external_value(PARAM_TEXT, 'Sort string', VALUE_DEFAULT, null), 'customfieldname' => new external_value(PARAM_ALPHANUMEXT, 'Used when classification = customfield', VALUE_DEFAULT, null), 'customfieldvalue' => new external_value(PARAM_RAW, 'Used when classification = customfield', VALUE_DEFAULT, null), 'searchvalue' => new external_value(PARAM_RAW, 'The value a user wishes to search against', VALUE_DEFAULT, null), 'requiredfields' => new core_external\external_multiple_structure( new external_value(PARAM_ALPHANUMEXT, 'Field name to be included from the results', VALUE_DEFAULT), 'Array of the only field names that need to be returned. If empty, all fields will be returned.', VALUE_DEFAULT, [] ), ) ); } /** * Get courses matching the given timeline classification. * * NOTE: The offset applies to the unfiltered full set of courses before the classification * filtering is done. * E.g. * If the user is enrolled in 5 courses: * c1, c2, c3, c4, and c5 * And c4 and c5 are 'future' courses * * If a request comes in for future courses with an offset of 1 it will mean that * c1 is skipped (because the offset applies *before* the classification filtering) * and c4 and c5 will be return. * * @param string $classification past, inprogress, or future * @param int $limit Result set limit * @param int $offset Offset the full course set before timeline classification is applied * @param string|null $sort SQL sort string for results * @param string|null $customfieldname * @param string|null $customfieldvalue * @param string|null $searchvalue * @param array $requiredfields Array of the only field names that need to be returned. If empty, all fields will be returned. * @return array list of courses and warnings */ public static function get_enrolled_courses_by_timeline_classification( string $classification, int $limit = 0, int $offset = 0, ?string $sort = null, ?string $customfieldname = null, ?string $customfieldvalue = null, ?string $searchvalue = null, array $requiredfields = [] ) { global $CFG, $PAGE, $USER; require_once($CFG->dirroot . '/course/lib.php'); $params = self::validate_parameters(self::get_enrolled_courses_by_timeline_classification_parameters(), array( 'classification' => $classification, 'limit' => $limit, 'offset' => $offset, 'sort' => $sort, 'customfieldvalue' => $customfieldvalue, 'searchvalue' => $searchvalue, 'requiredfields' => $requiredfields, ) ); $classification = $params['classification']; $limit = $params['limit']; $offset = $params['offset']; $sort = $params['sort']; $customfieldvalue = $params['customfieldvalue']; $searchvalue = clean_param($params['searchvalue'], PARAM_TEXT); $requiredfields = $params['requiredfields']; switch($classification) { case COURSE_TIMELINE_ALLINCLUDINGHIDDEN: break; case COURSE_TIMELINE_ALL: break; case COURSE_TIMELINE_PAST: break; case COURSE_TIMELINE_INPROGRESS: break; case COURSE_TIMELINE_FUTURE: break; case COURSE_FAVOURITES: break; case COURSE_TIMELINE_HIDDEN: break; case COURSE_TIMELINE_SEARCH: break; case COURSE_CUSTOMFIELD: break; default: throw new invalid_parameter_exception('Invalid classification'); } self::validate_context(context_user::instance($USER->id)); $exporterfields = array_keys(course_summary_exporter::define_properties()); // Get the required properties from the exporter fields based on the required fields. $requiredproperties = array_intersect($exporterfields, $requiredfields); // If the resulting required properties is empty, fall back to the exporter fields. if (empty($requiredproperties)) { $requiredproperties = $exporterfields; } $fields = join(',', $requiredproperties); $hiddencourses = get_hidden_courses_on_timeline(); // If the timeline requires really all courses, get really all courses. if ($classification == COURSE_TIMELINE_ALLINCLUDINGHIDDEN) { $courses = course_get_enrolled_courses_for_logged_in_user(0, $offset, $sort, $fields, COURSE_DB_QUERY_LIMIT); // Otherwise if the timeline requires the hidden courses then restrict the result to only $hiddencourses. } else if ($classification == COURSE_TIMELINE_HIDDEN) { $courses = course_get_enrolled_courses_for_logged_in_user(0, $offset, $sort, $fields, COURSE_DB_QUERY_LIMIT, $hiddencourses); // Otherwise get the requested courses and exclude the hidden courses. } else if ($classification == COURSE_TIMELINE_SEARCH) { // Prepare the search API options. $searchcriteria['search'] = $searchvalue; $options = ['idonly' => true]; $courses = course_get_enrolled_courses_for_logged_in_user_from_search( 0, $offset, $sort, $fields, COURSE_DB_QUERY_LIMIT, $searchcriteria, $options ); } else { $courses = course_get_enrolled_courses_for_logged_in_user(0, $offset, $sort, $fields, COURSE_DB_QUERY_LIMIT, [], $hiddencourses); } $favouritecourseids = []; $ufservice = \core_favourites\service_factory::get_service_for_user_context(\context_user::instance($USER->id)); $favourites = $ufservice->find_favourites_by_type('core_course', 'courses'); if ($favourites) { $favouritecourseids = array_map( function($favourite) { return $favourite->itemid; }, $favourites); } if ($classification == COURSE_FAVOURITES) { list($filteredcourses, $processedcount) = course_filter_courses_by_favourites( $courses, $favouritecourseids, $limit ); } else if ($classification == COURSE_CUSTOMFIELD) { list($filteredcourses, $processedcount) = course_filter_courses_by_customfield( $courses, $customfieldname, $customfieldvalue, $limit ); } else { list($filteredcourses, $processedcount) = course_filter_courses_by_timeline_classification( $courses, $classification, $limit ); } $renderer = $PAGE->get_renderer('core'); $formattedcourses = array_map(function($course) use ($renderer, $favouritecourseids) { if ($course == null) { return; } context_helper::preload_from_record($course); $context = context_course::instance($course->id); $isfavourite = false; if (in_array($course->id, $favouritecourseids)) { $isfavourite = true; } $exporter = new course_summary_exporter($course, ['context' => $context, 'isfavourite' => $isfavourite]); return $exporter->export($renderer); }, $filteredcourses); $formattedcourses = array_filter($formattedcourses, function($course) { if ($course != null) { return $course; } }); return [ 'courses' => $formattedcourses, 'nextoffset' => $offset + $processedcount ]; } /** * Returns description of method result value * * @return \core_external\external_description */ public static function get_enrolled_courses_by_timeline_classification_returns() { return new external_single_structure( array( 'courses' => new external_multiple_structure(course_summary_exporter::get_read_structure(), 'Course'), 'nextoffset' => new external_value(PARAM_INT, 'Offset for the next request') ) ); } /** * Returns description of method parameters * * @return external_function_parameters */ public static function set_favourite_courses_parameters() { return new external_function_parameters( array( 'courses' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'course ID'), 'favourite' => new external_value(PARAM_BOOL, 'favourite status') ) ) ) ) ); } /** * Set the course favourite status for an array of courses. * * @param array $courses List with course id's and favourite status. * @return array Array with an array of favourite courses. */ public static function set_favourite_courses( array $courses ) { global $USER; $params = self::validate_parameters(self::set_favourite_courses_parameters(), array( 'courses' => $courses ) ); $warnings = []; $ufservice = \core_favourites\service_factory::get_service_for_user_context(\context_user::instance($USER->id)); foreach ($params['courses'] as $course) { $warning = []; $favouriteexists = $ufservice->favourite_exists('core_course', 'courses', $course['id'], \context_course::instance($course['id'])); if ($course['favourite']) { if (!$favouriteexists) { try { $ufservice->create_favourite('core_course', 'courses', $course['id'], \context_course::instance($course['id'])); } catch (Exception $e) { $warning['courseid'] = $course['id']; if ($e instanceof moodle_exception) { $warning['warningcode'] = $e->errorcode; } else { $warning['warningcode'] = $e->getCode(); } $warning['message'] = $e->getMessage(); $warnings[] = $warning; $warnings[] = $warning; } } else { $warning['courseid'] = $course['id']; $warning['warningcode'] = 'coursealreadyfavourited'; $warning['message'] = 'Course already favourited'; $warnings[] = $warning; } } else { if ($favouriteexists) { try { $ufservice->delete_favourite('core_course', 'courses', $course['id'], \context_course::instance($course['id'])); } catch (Exception $e) { $warning['courseid'] = $course['id']; if ($e instanceof moodle_exception) { $warning['warningcode'] = $e->errorcode; } else { $warning['warningcode'] = $e->getCode(); } $warning['message'] = $e->getMessage(); $warnings[] = $warning; $warnings[] = $warning; } } else { $warning['courseid'] = $course['id']; $warning['warningcode'] = 'cannotdeletefavourite'; $warning['message'] = 'Could not delete favourite status for course'; $warnings[] = $warning; } } } return [ 'warnings' => $warnings ]; } /** * Returns description of method result value * * @return \core_external\external_description */ public static function set_favourite_courses_returns() { return new external_single_structure( array( 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.6 */ public static function get_recent_courses_parameters() { return new external_function_parameters( array( 'userid' => new external_value(PARAM_INT, 'id of the user, default to current user', VALUE_DEFAULT, 0), 'limit' => new external_value(PARAM_INT, 'result set limit', VALUE_DEFAULT, 0), 'offset' => new external_value(PARAM_INT, 'Result set offset', VALUE_DEFAULT, 0), 'sort' => new external_value(PARAM_TEXT, 'Sort string', VALUE_DEFAULT, null) ) ); } /** * Get last accessed courses adding additional course information like images. * * @param int $userid User id from which the courses will be obtained * @param int $limit Restrict result set to this amount * @param int $offset Skip this number of records from the start of the result set * @param string|null $sort SQL string for sorting * @return array List of courses * @throws invalid_parameter_exception */ public static function get_recent_courses(int $userid = 0, int $limit = 0, int $offset = 0, ?string $sort = null) { global $USER, $PAGE; if (empty($userid)) { $userid = $USER->id; } $params = self::validate_parameters(self::get_recent_courses_parameters(), array( 'userid' => $userid, 'limit' => $limit, 'offset' => $offset, 'sort' => $sort ) ); $userid = $params['userid']; $limit = $params['limit']; $offset = $params['offset']; $sort = $params['sort']; $usercontext = context_user::instance($userid); self::validate_context($usercontext); if ($userid != $USER->id && !has_capability('moodle/user:viewalldetails', $usercontext)) { return array(); } $courses = course_get_recent_courses($userid, $limit, $offset, $sort); $renderer = $PAGE->get_renderer('core'); $recentcourses = array_map(function($course) use ($renderer) { context_helper::preload_from_record($course); $context = context_course::instance($course->id); $isfavourite = !empty($course->component); $exporter = new course_summary_exporter($course, ['context' => $context, 'isfavourite' => $isfavourite]); return $exporter->export($renderer); }, $courses); return $recentcourses; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 3.6 */ public static function get_recent_courses_returns() { return new external_multiple_structure(course_summary_exporter::get_read_structure(), 'Courses'); } /** * Returns description of method parameters * * @return external_function_parameters */ public static function get_enrolled_users_by_cmid_parameters() { return new external_function_parameters([ 'cmid' => new external_value(PARAM_INT, 'id of the course module', VALUE_REQUIRED), 'groupid' => new external_value(PARAM_INT, 'id of the group', VALUE_DEFAULT, 0), 'onlyactive' => new external_value(PARAM_BOOL, 'whether to return only active users or all.', VALUE_DEFAULT, false), ]); } /** * Get all users in a course for a given cmid. * * @param int $cmid Course Module id from which the users will be obtained * @param int $groupid Group id from which the users will be obtained * @param bool $onlyactive Whether to return only the active enrolled users or all enrolled users in the course. * @return array List of users */ public static function get_enrolled_users_by_cmid(int $cmid, int $groupid = 0, bool $onlyactive = false) { global $PAGE; $warnings = []; [ 'cmid' => $cmid, 'groupid' => $groupid, 'onlyactive' => $onlyactive, ] = self::validate_parameters(self::get_enrolled_users_by_cmid_parameters(), [ 'cmid' => $cmid, 'groupid' => $groupid, 'onlyactive' => $onlyactive, ]); list($course, $cm) = get_course_and_cm_from_cmid($cmid); $coursecontext = context_course::instance($course->id); self::validate_context($coursecontext); course_require_view_participants($coursecontext); $enrolledusers = get_enrolled_users($coursecontext, '', $groupid, 'u.*', null, 0, 0, $onlyactive); $users = array_map(function ($user) use ($PAGE) { $user->fullname = fullname($user); $userpicture = new user_picture($user); $userpicture->size = 1; $user->profileimage = $userpicture->get_url($PAGE)->out(false); return $user; }, $enrolledusers); sort($users); return [ 'users' => $users, 'warnings' => $warnings, ]; } /** * Returns description of method result value * * @return \core_external\external_description */ public static function get_enrolled_users_by_cmid_returns() { return new external_single_structure([ 'users' => new external_multiple_structure(self::user_description()), 'warnings' => new external_warnings(), ]); } /** * Create user return value description. * * @return \core_external\external_description */ public static function user_description() { $userfields = array( 'id' => new external_value(core_user::get_property_type('id'), 'ID of the user'), 'profileimage' => new external_value(PARAM_URL, 'The location of the users larger image', VALUE_OPTIONAL), 'fullname' => new external_value(PARAM_TEXT, 'The full name of the user', VALUE_OPTIONAL), 'firstname' => new external_value( core_user::get_property_type('firstname'), 'The first name(s) of the user', VALUE_OPTIONAL), 'lastname' => new external_value( core_user::get_property_type('lastname'), 'The family name of the user', VALUE_OPTIONAL), ); return new external_single_structure($userfields); } /** * Returns description of method parameters. * * @return external_function_parameters */ public static function add_content_item_to_user_favourites_parameters() { return new external_function_parameters([ 'componentname' => new external_value(PARAM_TEXT, 'frankenstyle name of the component to which the content item belongs', VALUE_REQUIRED), 'contentitemid' => new external_value(PARAM_INT, 'id of the content item', VALUE_REQUIRED, '', NULL_NOT_ALLOWED) ]); } /** * Add a content item to a user's favourites. * * @param string $componentname the name of the component from which this content item originates. * @param int $contentitemid the id of the content item. * @return stdClass the exporter content item. */ public static function add_content_item_to_user_favourites(string $componentname, int $contentitemid) { global $USER; [ 'componentname' => $componentname, 'contentitemid' => $contentitemid, ] = self::validate_parameters(self::add_content_item_to_user_favourites_parameters(), [ 'componentname' => $componentname, 'contentitemid' => $contentitemid, ] ); self::validate_context(context_user::instance($USER->id)); $contentitemservice = \core_course\local\factory\content_item_service_factory::get_content_item_service(); return $contentitemservice->add_to_user_favourites($USER, $componentname, $contentitemid); } /** * Returns description of method result value. * * @return \core_external\external_description */ public static function add_content_item_to_user_favourites_returns() { return \core_course\local\exporters\course_content_item_exporter::get_read_structure(); } /** * Returns description of method parameters. * * @return external_function_parameters */ public static function remove_content_item_from_user_favourites_parameters() { return new external_function_parameters([ 'componentname' => new external_value(PARAM_TEXT, 'frankenstyle name of the component to which the content item belongs', VALUE_REQUIRED), 'contentitemid' => new external_value(PARAM_INT, 'id of the content item', VALUE_REQUIRED, '', NULL_NOT_ALLOWED), ]); } /** * Remove a content item from a user's favourites. * * @param string $componentname the name of the component from which this content item originates. * @param int $contentitemid the id of the content item. * @return stdClass the exported content item. */ public static function remove_content_item_from_user_favourites(string $componentname, int $contentitemid) { global $USER; [ 'componentname' => $componentname, 'contentitemid' => $contentitemid, ] = self::validate_parameters(self::remove_content_item_from_user_favourites_parameters(), [ 'componentname' => $componentname, 'contentitemid' => $contentitemid, ] ); self::validate_context(context_user::instance($USER->id)); $contentitemservice = \core_course\local\factory\content_item_service_factory::get_content_item_service(); return $contentitemservice->remove_from_user_favourites($USER, $componentname, $contentitemid); } /** * Returns description of method result value. * * @return \core_external\external_description */ public static function remove_content_item_from_user_favourites_returns() { return \core_course\local\exporters\course_content_item_exporter::get_read_structure(); } /** * Returns description of method result value * * @return \core_external\external_description */ public static function get_course_content_items_returns() { return new external_single_structure([ 'content_items' => new external_multiple_structure( \core_course\local\exporters\course_content_item_exporter::get_read_structure() ), ]); } /** * Returns description of method parameters * * @return external_function_parameters */ public static function get_course_content_items_parameters() { return new external_function_parameters([ 'courseid' => new external_value(PARAM_INT, 'ID of the course', VALUE_REQUIRED), 'sectionnum' => new external_value(PARAM_INT, 'Number of the section', VALUE_DEFAULT), ]); } /** * Given a course ID fetch all accessible modules for that course * * @param int $courseid The course we want to fetch the modules for * @param int|null $sectionnum The section we want to fetch the modules for * @return array Contains array of modules and their metadata */ public static function get_course_content_items(int $courseid, ?int $sectionnum = null) { global $USER; [ 'courseid' => $courseid, 'sectionnum' => $sectionnum, ] = self::validate_parameters(self::get_course_content_items_parameters(), [ 'courseid' => $courseid, 'sectionnum' => $sectionnum, ]); $coursecontext = context_course::instance($courseid); self::validate_context($coursecontext); $course = get_course($courseid); // Get section_info object with all delegation information. $sectioninfo = get_fast_modinfo($course)->get_section_info($sectionnum); $contentitemservice = \core_course\local\factory\content_item_service_factory::get_content_item_service(); $contentitems = $contentitemservice->get_content_items_for_user_in_course($USER, $course, [], $sectioninfo); return ['content_items' => $contentitems]; } /** * Returns description of method parameters. * * @return external_function_parameters */ public static function toggle_activity_recommendation_parameters() { return new external_function_parameters([ 'area' => new external_value(PARAM_TEXT, 'The favourite area (itemtype)', VALUE_REQUIRED), 'id' => new external_value(PARAM_INT, 'id of the activity or whatever', VALUE_REQUIRED), ]); } /** * Update the recommendation for an activity item. * * @param string $area identifier for this activity. * @param int $id Associated id. This is needed in conjunction with the area to find the recommendation. * @return array some warnings or something. */ public static function toggle_activity_recommendation(string $area, int $id): array { ['area' => $area, 'id' => $id] = self::validate_parameters(self::toggle_activity_recommendation_parameters(), ['area' => $area, 'id' => $id]); $context = context_system::instance(); self::validate_context($context); require_capability('moodle/course:recommendactivity', $context); $manager = \core_course\local\factory\content_item_service_factory::get_content_item_service(); $status = $manager->toggle_recommendation($area, $id); return ['id' => $id, 'area' => $area, 'status' => $status]; } /** * Returns warnings. * * @return \core_external\external_description */ public static function toggle_activity_recommendation_returns() { return new external_single_structure( [ 'id' => new external_value(PARAM_INT, 'id of the activity or whatever'), 'area' => new external_value(PARAM_TEXT, 'The favourite area (itemtype)'), 'status' => new external_value(PARAM_BOOL, 'If created or deleted'), ] ); } /** * Returns description of method parameters * * @return external_function_parameters */ public static function get_activity_chooser_footer_parameters() { return new external_function_parameters([ 'courseid' => new external_value(PARAM_INT, 'ID of the course', VALUE_REQUIRED), 'sectionid' => new external_value(PARAM_INT, 'ID of the section', VALUE_REQUIRED), ]); } /** * Given a course ID we need to build up a footre for the chooser. * * @param int $courseid The course we want to fetch the modules for * @param int $sectionid The section we want to fetch the modules for * @return array */ public static function get_activity_chooser_footer(int $courseid, int $sectionid) { [ 'courseid' => $courseid, 'sectionid' => $sectionid, ] = self::validate_parameters(self::get_activity_chooser_footer_parameters(), [ 'courseid' => $courseid, 'sectionid' => $sectionid, ]); $coursecontext = context_course::instance($courseid); self::validate_context($coursecontext); // The active plugin must be set, and be present on the site. $activeplugin = clean_param( get_config('core', 'activitychooseractivefooter'), PARAM_COMPONENT, ); if ( $activeplugin !== COURSE_CHOOSER_FOOTER_NONE && !empty($activeplugin) && core_component::get_component_directory($activeplugin) !== null ) { $footerdata = component_callback($activeplugin, 'custom_chooser_footer', [$courseid, $sectionid]); return [ 'footer' => true, 'customfooterjs' => $footerdata->get_footer_js_file(), 'customfootertemplate' => $footerdata->get_footer_template(), 'customcarouseltemplate' => $footerdata->get_carousel_template(), ]; } else { return [ 'footer' => false, ]; } } /** * Returns description of method result value * * @return \core_external\external_description */ public static function get_activity_chooser_footer_returns() { return new external_single_structure( [ 'footer' => new external_value(PARAM_BOOL, 'Is a footer being return by this request?', VALUE_REQUIRED), 'customfooterjs' => new external_value(PARAM_RAW, 'The path to the plugin JS file', VALUE_OPTIONAL), 'customfootertemplate' => new external_value(PARAM_RAW, 'The prerendered footer', VALUE_OPTIONAL), 'customcarouseltemplate' => new external_value(PARAM_RAW, 'Either "" or the prerendered carousel page', VALUE_OPTIONAL), ] ); } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/enrol/self/externallib.php000064400000023126152266577470024004 0ustar00. use core_external\external_api; use core_external\external_function_parameters; use core_external\external_single_structure; use core_external\external_value; use core_external\external_warnings; /** * Self enrolment external functions. * * @package enrol_self * @copyright 2012 Rajesh Taneja * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.6 */ class enrol_self_external extends external_api { /** * Returns description of get_instance_info() parameters. * * @return external_function_parameters */ public static function get_instance_info_parameters() { return new external_function_parameters([ 'instanceid' => new external_value(PARAM_INT, 'instance id of self enrolment plugin.'), ]); } /** * Return self-enrolment instance information. * * @param int $instanceid instance id of self enrolment plugin. * @return array instance information. * @throws moodle_exception */ public static function get_instance_info($instanceid) { global $DB, $CFG; require_once($CFG->libdir . '/enrollib.php'); $params = self::validate_parameters(self::get_instance_info_parameters(), array('instanceid' => $instanceid)); // Retrieve self enrolment plugin. $enrolplugin = enrol_get_plugin('self'); if (empty($enrolplugin)) { throw new moodle_exception('invaliddata', 'error'); } self::validate_context(context_system::instance()); $enrolinstance = $DB->get_record('enrol', array('id' => $params['instanceid']), '*', MUST_EXIST); $course = $DB->get_record('course', array('id' => $enrolinstance->courseid), '*', MUST_EXIST); if (!core_course_category::can_view_course_info($course) && !can_access_course($course)) { throw new moodle_exception('coursehidden'); } $instanceinfo = (array) $enrolplugin->get_enrol_info($enrolinstance); if (isset($instanceinfo['requiredparam']->enrolpassword)) { $instanceinfo['enrolpassword'] = $instanceinfo['requiredparam']->enrolpassword; } unset($instanceinfo->requiredparam); return $instanceinfo; } /** * Returns description of get_instance_info() result value. * * @return \core_external\external_description */ public static function get_instance_info_returns() { return new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'id of course enrolment instance'), 'courseid' => new external_value(PARAM_INT, 'id of course'), 'type' => new external_value(PARAM_PLUGIN, 'type of enrolment plugin'), 'name' => new external_value(PARAM_RAW, 'name of enrolment plugin'), 'status' => new external_value(PARAM_RAW, 'status of enrolment plugin'), 'enrolpassword' => new external_value(PARAM_RAW, 'password required for enrolment', VALUE_OPTIONAL), ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.0 */ public static function enrol_user_parameters() { return new external_function_parameters( array( 'courseid' => new external_value(PARAM_INT, 'Id of the course'), 'password' => new external_value(PARAM_RAW, 'Enrolment key', VALUE_DEFAULT, ''), 'instanceid' => new external_value(PARAM_INT, 'Instance id of self enrolment plugin.', VALUE_DEFAULT, 0) ) ); } /** * Self enrol the current user in the given course. * * @param int $courseid id of course * @param string $password enrolment key * @param int $instanceid instance id of self enrolment plugin * @return array of warnings and status result * @since Moodle 3.0 * @throws moodle_exception */ public static function enrol_user($courseid, $password = '', $instanceid = 0) { global $CFG; require_once($CFG->libdir . '/enrollib.php'); $params = self::validate_parameters(self::enrol_user_parameters(), array( 'courseid' => $courseid, 'password' => $password, 'instanceid' => $instanceid )); $warnings = array(); $course = get_course($params['courseid']); $context = context_course::instance($course->id); self::validate_context(context_system::instance()); if (!core_course_category::can_view_course_info($course)) { throw new moodle_exception('coursehidden'); } // Retrieve the self enrolment plugin. $enrol = enrol_get_plugin('self'); if (empty($enrol)) { throw new moodle_exception('canntenrol', 'enrol_self'); } // We can expect multiple self-enrolment instances. $instances = array(); $enrolinstances = enrol_get_instances($course->id, true); foreach ($enrolinstances as $courseenrolinstance) { if ($courseenrolinstance->enrol == "self") { // Instance specified. if (!empty($params['instanceid'])) { if ($courseenrolinstance->id == $params['instanceid']) { $instances[] = $courseenrolinstance; break; } } else { $instances[] = $courseenrolinstance; } } } if (empty($instances)) { throw new moodle_exception('canntenrol', 'enrol_self'); } // Try to enrol the user in the instance/s. $enrolled = false; foreach ($instances as $instance) { $enrolstatus = $enrol->can_self_enrol($instance); if ($enrolstatus === true) { if ($instance->password and $params['password'] !== $instance->password) { // Check if we are using group enrolment keys. if ($instance->customint1) { require_once($CFG->dirroot . "/enrol/self/locallib.php"); if (!enrol_self_check_group_enrolment_key($course->id, $params['password'])) { $warnings[] = array( 'item' => 'instance', 'itemid' => $instance->id, 'warningcode' => '2', 'message' => get_string('passwordinvalid', 'enrol_self') ); continue; } } else { if ($enrol->get_config('showhint')) { $hint = core_text::substr($instance->password, 0, 1); $warnings[] = array( 'item' => 'instance', 'itemid' => $instance->id, 'warningcode' => '3', 'message' => s(get_string('passwordinvalidhint', 'enrol_self', $hint)) // message is PARAM_TEXT. ); continue; } else { $warnings[] = array( 'item' => 'instance', 'itemid' => $instance->id, 'warningcode' => '4', 'message' => get_string('passwordinvalid', 'enrol_self') ); continue; } } } // Do the enrolment. $data = array('enrolpassword' => $params['password']); $enrol->enrol_self($instance, (object) $data); $enrolled = true; break; } else { $warnings[] = array( 'item' => 'instance', 'itemid' => $instance->id, 'warningcode' => '1', 'message' => $enrolstatus ); } } $result = array(); $result['status'] = $enrolled; $result['warnings'] = $warnings; return $result; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 3.0 */ public static function enrol_user_returns() { return new external_single_structure( array( 'status' => new external_value(PARAM_BOOL, 'status: true if the user is enrolled, false otherwise'), 'warnings' => new external_warnings() ) ); } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/enrol/manual/externallib.php000064400000022147152266601020024305 0ustar00. use core_external\external_api; use core_external\external_function_parameters; use core_external\external_multiple_structure; use core_external\external_single_structure; use core_external\external_value; /** * External course participation api. * * This api is mostly read only, the actual enrol and unenrol * support is in each enrol plugin. * * @package enrol_manual * @category external * @copyright 2011 Jerome Mouneyrac * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.2 */ class enrol_manual_external extends external_api { /** * Returns description of method parameters. * * @return external_function_parameters * @since Moodle 2.2 */ public static function enrol_users_parameters() { return new external_function_parameters( array( 'enrolments' => new external_multiple_structure( new external_single_structure( array( 'roleid' => new external_value(PARAM_INT, 'Role to assign to the user'), 'userid' => new external_value(PARAM_INT, 'The user that is going to be enrolled'), 'courseid' => new external_value(PARAM_INT, 'The course to enrol the user role in'), 'timestart' => new external_value(PARAM_INT, 'Timestamp when the enrolment start', VALUE_OPTIONAL), 'timeend' => new external_value(PARAM_INT, 'Timestamp when the enrolment end', VALUE_OPTIONAL), 'suspend' => new external_value(PARAM_INT, 'set to 1 to suspend the enrolment', VALUE_OPTIONAL) ) ) ) ) ); } /** * Enrolment of users. * * Function throw an exception at the first error encountered. * @param array $enrolments An array of user enrolment * @since Moodle 2.2 */ public static function enrol_users($enrolments) { global $DB, $CFG; require_once($CFG->libdir . '/enrollib.php'); $params = self::validate_parameters(self::enrol_users_parameters(), array('enrolments' => $enrolments)); $transaction = $DB->start_delegated_transaction(); // Rollback all enrolment if an error occurs // (except if the DB doesn't support it). // Retrieve the manual enrolment plugin. $enrol = enrol_get_plugin('manual'); if (empty($enrol)) { throw new moodle_exception('manualpluginnotinstalled', 'enrol_manual'); } foreach ($params['enrolments'] as $enrolment) { // Ensure the current user is allowed to run this function in the enrolment context. $context = context_course::instance($enrolment['courseid'], IGNORE_MISSING); self::validate_context($context); // Check that the user has the permission to manual enrol. require_capability('enrol/manual:enrol', $context); $user = core_user::get_user($enrolment['userid'], strictness: MUST_EXIST); core_user::require_active_user($user); // Throw an exception if user is not able to assign the role. $roles = get_assignable_roles($context); if (!array_key_exists($enrolment['roleid'], $roles)) { $errorparams = new stdClass(); $errorparams->roleid = $enrolment['roleid']; $errorparams->courseid = $enrolment['courseid']; $errorparams->userid = $user->id; throw new moodle_exception('wsusercannotassign', 'enrol_manual', '', $errorparams); } // Check manual enrolment plugin instance is enabled/exist. $instance = null; $enrolinstances = enrol_get_instances($enrolment['courseid'], true); foreach ($enrolinstances as $courseenrolinstance) { if ($courseenrolinstance->enrol == "manual") { $instance = $courseenrolinstance; break; } } if (empty($instance)) { $errorparams = new stdClass(); $errorparams->courseid = $enrolment['courseid']; throw new moodle_exception('wsnoinstance', 'enrol_manual', '', $errorparams); } // Check that the plugin accept enrolment (it should always the case, it's hard coded in the plugin). if (!$enrol->allow_enrol($instance)) { $errorparams = new stdClass(); $errorparams->roleid = $enrolment['roleid']; $errorparams->courseid = $enrolment['courseid']; $errorparams->userid = $user->id; throw new moodle_exception('wscannotenrol', 'enrol_manual', '', $errorparams); } // Finally proceed the enrolment. $enrolment['timestart'] = isset($enrolment['timestart']) ? $enrolment['timestart'] : 0; $enrolment['timeend'] = isset($enrolment['timeend']) ? $enrolment['timeend'] : 0; $enrolment['status'] = (isset($enrolment['suspend']) && !empty($enrolment['suspend'])) ? ENROL_USER_SUSPENDED : ENROL_USER_ACTIVE; $enrol->enrol_user($instance, $user->id, $enrolment['roleid'], $enrolment['timestart'], $enrolment['timeend'], $enrolment['status']); } $transaction->allow_commit(); } /** * Returns description of method result value. * * @return null * @since Moodle 2.2 */ public static function enrol_users_returns() { return null; } /** * Returns description of method parameters. * * @return external_function_parameters */ public static function unenrol_users_parameters() { return new external_function_parameters(array( 'enrolments' => new external_multiple_structure( new external_single_structure( array( 'userid' => new external_value(PARAM_INT, 'The user that is going to be unenrolled'), 'courseid' => new external_value(PARAM_INT, 'The course to unenrol the user from'), 'roleid' => new external_value(PARAM_INT, 'The user role', VALUE_OPTIONAL), ) ) ) )); } /** * Unenrolment of users. * * @param array $enrolments an array of course user and role ids * @throws coding_exception * @throws dml_transaction_exception * @throws invalid_parameter_exception * @throws moodle_exception * @throws required_capability_exception * @throws restricted_context_exception */ public static function unenrol_users($enrolments) { global $CFG, $DB; $params = self::validate_parameters(self::unenrol_users_parameters(), array('enrolments' => $enrolments)); require_once($CFG->libdir . '/enrollib.php'); $transaction = $DB->start_delegated_transaction(); // Rollback all enrolment if an error occurs. $enrol = enrol_get_plugin('manual'); if (empty($enrol)) { throw new moodle_exception('manualpluginnotinstalled', 'enrol_manual'); } foreach ($params['enrolments'] as $enrolment) { $context = context_course::instance($enrolment['courseid']); self::validate_context($context); require_capability('enrol/manual:unenrol', $context); $instance = $DB->get_record('enrol', array('courseid' => $enrolment['courseid'], 'enrol' => 'manual')); if (!$instance) { throw new moodle_exception('wsnoinstance', 'enrol_manual', '', $enrolment); } $user = core_user::get_user($enrolment['userid'], strictness: MUST_EXIST); core_user::require_active_user($user); if (!$enrol->allow_unenrol($instance)) { throw new moodle_exception('wscannotunenrol', 'enrol_manual', '', $enrolment); } $enrol->unenrol_user($instance, $user->id); } $transaction->allow_commit(); } /** * Returns description of method result value. * * @return null */ public static function unenrol_users_returns() { return null; } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/files/externallib.php000064400000040122152266601520023011 0ustar00. /** * External files API * * @package core_files * @category external * @copyright 2010 Dongsheng Cai * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ use core_external\external_api; use core_external\external_function_parameters; use core_external\external_multiple_structure; use core_external\external_single_structure; use core_external\external_value; defined('MOODLE_INTERNAL') || die(); require_once("{$CFG->libdir}/filelib.php"); /** * Files external functions * * @package core_files * @category external * @copyright 2011 Jerome Mouneyrac * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.2 */ class core_files_external extends external_api { /** * Returns description of get_files parameters * * @return external_function_parameters * @since Moodle 2.2 */ public static function get_files_parameters() { return new external_function_parameters([ 'contextid' => new external_value(PARAM_INT, 'context id Set to -1 to use contextlevel and instanceid.'), 'component' => new external_value(PARAM_TEXT, 'component'), 'filearea' => new external_value(PARAM_TEXT, 'file area'), 'itemid' => new external_value(PARAM_INT, 'associated id'), 'filepath' => new external_value(PARAM_PATH, 'file path'), 'filename' => new external_value(PARAM_TEXT, 'file name'), 'modified' => new external_value( PARAM_INT, 'timestamp to return files changed after this time.', VALUE_DEFAULT, null ), 'contextlevel' => new external_value(PARAM_ALPHA, 'The context level for the file location.', VALUE_DEFAULT, null), 'instanceid' => new external_value(PARAM_INT, 'The instance id for where the file is located.', VALUE_DEFAULT, null), ]); } /** * Return moodle files listing * * @param int $contextid context id * @param int $component component * @param int $filearea file area * @param int $itemid item id * @param string $filepath file path * @param string $filename file name * @param int $modified timestamp to return files changed after this time. * @param string $contextlevel The context level for the file location. * @param int $instanceid The instance id for where the file is located. * @return array * @since Moodle 2.9 Returns additional fields (timecreated, filesize, author, license) * @since Moodle 2.2 */ public static function get_files($contextid, $component, $filearea, $itemid, $filepath, $filename, $modified = null, $contextlevel = null, $instanceid = null) { $parameters = [ 'contextid' => $contextid, 'component' => $component, 'filearea' => $filearea, 'itemid' => $itemid, 'filepath' => $filepath, 'filename' => $filename, 'modified' => $modified, 'contextlevel' => $contextlevel, 'instanceid' => $instanceid, ]; $fileinfo = self::validate_parameters(self::get_files_parameters(), $parameters); $browser = get_file_browser(); // We need to preserve backwards compatibility. Zero will use the system context and minus one will // use the addtional parameters to determine the context. // TODO MDL-40489 get_context_from_params should handle this logic. if ($fileinfo['contextid'] == 0) { $context = context_system::instance(); } else { if ($fileinfo['contextid'] == -1) { $fileinfo['contextid'] = null; } $context = self::get_context_from_params($fileinfo); } self::validate_context($context); if (empty($fileinfo['component'])) { $fileinfo['component'] = null; } if (empty($fileinfo['filearea'])) { $fileinfo['filearea'] = null; } if (empty($fileinfo['filename'])) { $fileinfo['filename'] = null; } if (empty($fileinfo['filepath'])) { $fileinfo['filepath'] = null; } $return = array(); $return['parents'] = array(); $return['files'] = array(); $list = array(); if ($file = $browser->get_file_info( $context, $fileinfo['component'], $fileinfo['filearea'], $fileinfo['itemid'], $fileinfo['filepath'], $fileinfo['filename'])) { $level = $file->get_parent(); while ($level) { $params = $level->get_params(); $params['filename'] = $level->get_visible_name(); array_unshift($return['parents'], $params); $level = $level->get_parent(); } $children = $file->get_children(); foreach ($children as $child) { $params = $child->get_params(); $timemodified = $child->get_timemodified(); $timecreated = $child->get_timecreated(); if ($child->is_directory()) { if ((is_null($modified)) or ($modified < $timemodified)) { $node = [ 'contextid' => $params['contextid'], 'component' => $params['component'], 'filearea' => $params['filearea'], 'itemid' => $params['itemid'], 'filepath' => $params['filepath'], 'filename' => $child->get_visible_name(), 'url' => null, 'isdir' => true, 'timemodified' => $timemodified, 'timecreated' => $timecreated, 'filesize' => 0, 'author' => null, 'license' => null, ]; $list[] = $node; } } else { if ((is_null($modified)) or ($modified < $timemodified)) { $node = [ 'contextid' => $params['contextid'], 'component' => $params['component'], 'filearea' => $params['filearea'], 'itemid' => $params['itemid'], 'filepath' => $params['filepath'], 'filename' => $child->get_visible_name(), 'url' => $child->get_url(), 'isdir' => false, 'timemodified' => $timemodified, 'timecreated' => $timecreated, 'filesize' => $child->get_filesize(), 'author' => $child->get_author(), 'license' => $child->get_license(), ]; $list[] = $node; } } } } $return['files'] = $list; return $return; } /** * Returns description of get_files returns * * @return external_single_structure * @since Moodle 2.9 Returns additional fields for files (timecreated, filesize, author, license) * @since Moodle 2.2 */ public static function get_files_returns() { return new external_single_structure([ 'parents' => new external_multiple_structure( new external_single_structure([ 'contextid' => new external_value(PARAM_INT, ''), 'component' => new external_value(PARAM_COMPONENT, ''), 'filearea' => new external_value(PARAM_AREA, ''), 'itemid' => new external_value(PARAM_INT, ''), 'filepath' => new external_value(PARAM_TEXT, ''), 'filename' => new external_value(PARAM_TEXT, ''), ]) ), 'files' => new external_multiple_structure( new external_single_structure([ 'contextid' => new external_value(PARAM_INT, ''), 'component' => new external_value(PARAM_COMPONENT, ''), 'filearea' => new external_value(PARAM_AREA, ''), 'itemid' => new external_value(PARAM_INT, ''), 'filepath' => new external_value(PARAM_TEXT, ''), 'filename' => new external_value(PARAM_TEXT, ''), 'isdir' => new external_value(PARAM_BOOL, ''), 'url' => new external_value(PARAM_TEXT, ''), 'timemodified' => new external_value(PARAM_INT, ''), 'timecreated' => new external_value(PARAM_INT, 'Time created', VALUE_OPTIONAL), 'filesize' => new external_value(PARAM_INT, 'File size', VALUE_OPTIONAL), 'author' => new external_value(PARAM_TEXT, 'File owner', VALUE_OPTIONAL), 'license' => new external_value(PARAM_TEXT, 'File license', VALUE_OPTIONAL), ]) ), ]); } /** * Returns description of upload parameters * * @return external_function_parameters * @since Moodle 2.2 */ public static function upload_parameters() { return new external_function_parameters([ 'contextid' => new external_value(PARAM_INT, 'context id', VALUE_DEFAULT, null), 'component' => new external_value(PARAM_COMPONENT, 'component'), 'filearea' => new external_value(PARAM_AREA, 'file area'), 'itemid' => new external_value(PARAM_INT, 'associated id'), 'filepath' => new external_value(PARAM_PATH, 'file path'), 'filename' => new external_value(PARAM_FILE, 'file name'), 'filecontent' => new external_value(PARAM_TEXT, 'file content'), 'contextlevel' => new external_value( PARAM_ALPHA, 'The context level to put the file in, (block, course, coursecat, system, user, module)', VALUE_DEFAULT, null ), 'instanceid' => new external_value(PARAM_INT, 'The Instance id of item associated with the context level', VALUE_DEFAULT, null), ]); } /** * Uploading a file to moodle * * @param int $contextid context id * @param string $component component * @param string $filearea file area * @param int $itemid item id * @param string $filepath file path * @param string $filename file name * @param string $filecontent file content * @param string $contextlevel Context level (block, course, coursecat, system, user or module) * @param int $instanceid Instance id of the item associated with the context level * @return array * @since Moodle 2.2 */ public static function upload( $contextid, $component, $filearea, $itemid, $filepath, $filename, $filecontent, $contextlevel, $instanceid ) { global $USER, $CFG; $fileinfo = self::validate_parameters(self::upload_parameters(), [ 'contextid' => $contextid, 'component' => $component, 'filearea' => $filearea, 'itemid' => $itemid, 'filepath' => $filepath, 'filename' => $filename, 'filecontent' => $filecontent, 'contextlevel' => $contextlevel, 'instanceid' => $instanceid, ]); if (!isset($fileinfo['filecontent'])) { throw new moodle_exception('nofile'); } // Saving file. $dir = make_temp_directory('wsupload'); if (empty($fileinfo['filename'])) { $filename = uniqid('wsupload', true).'_'.time().'.tmp'; } else { $filename = $fileinfo['filename']; } if (file_exists($dir.$filename)) { $savedfilepath = $dir.uniqid('m').$filename; } else { $savedfilepath = $dir.$filename; } file_put_contents($savedfilepath, base64_decode($fileinfo['filecontent'])); @chmod($savedfilepath, $CFG->filepermissions); unset($fileinfo['filecontent']); if (!empty($fileinfo['filepath'])) { $filepath = $fileinfo['filepath']; } else { $filepath = '/'; } // Only allow uploads to draft area. if (!($fileinfo['component'] == 'user' and $fileinfo['filearea'] == 'draft')) { throw new coding_exception('File can be uploaded to user draft area only'); } else { $component = 'user'; $filearea = $fileinfo['filearea']; } $itemid = 0; if (isset($fileinfo['itemid'])) { $itemid = $fileinfo['itemid']; } if ($filearea == 'draft' && $itemid <= 0) { // Generate a draft area for the files. $itemid = file_get_unused_draft_itemid(); } else if ($filearea == 'private') { // TODO MDL-31116 in user private area, itemid is always 0. $itemid = 0; } // We need to preserve backword compatibility. Context id is no more a required. if (empty($fileinfo['contextid'])) { unset($fileinfo['contextid']); } // Get and validate context. $context = self::get_context_from_params($fileinfo); self::validate_context($context); if (($fileinfo['component'] == 'user' and $fileinfo['filearea'] == 'private')) { throw new moodle_exception('privatefilesupload'); } $browser = get_file_browser(); // Check existing file. if ($file = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename)) { throw new moodle_exception('fileexist'); } // Move file to filepool. if ($dir = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, '.')) { $info = $dir->create_file_from_pathname($filename, $savedfilepath); $params = $info->get_params(); unlink($savedfilepath); return [ 'contextid' => $params['contextid'], 'component' => $params['component'], 'filearea' => $params['filearea'], 'itemid' => $params['itemid'], 'filepath' => $params['filepath'], 'filename' => $params['filename'], 'url' => $info->get_url(), ]; } else { throw new moodle_exception('nofile'); } } /** * Returns description of upload returns * * @return external_single_structure * @since Moodle 2.2 */ public static function upload_returns() { return new external_single_structure([ 'contextid' => new external_value(PARAM_INT, ''), 'component' => new external_value(PARAM_COMPONENT, ''), 'filearea' => new external_value(PARAM_AREA, ''), 'itemid' => new external_value(PARAM_INT, ''), 'filepath' => new external_value(PARAM_TEXT, ''), 'filename' => new external_value(PARAM_FILE, ''), 'url' => new external_value(PARAM_TEXT, ''), ]); } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/mod/assign/externallib.php000064400000410074152267114260023762 0ustar00. /** * External assign API * * @package mod_assign * @since Moodle 2.4 * @copyright 2012 Paul Charsley * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ use core_external\external_files; use core_external\external_format_value; use core_external\external_function_parameters; use core_external\external_multiple_structure; use core_external\external_single_structure; use core_external\external_value; use core_external\external_warnings; use core_external\util as external_util; defined('MOODLE_INTERNAL') || die; require_once("$CFG->dirroot/user/externallib.php"); require_once("$CFG->dirroot/mod/assign/locallib.php"); /** * Assign functions * @copyright 2012 Paul Charsley * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class mod_assign_external extends \mod_assign\external\external_api { /** * Describes the parameters for get_grades * @return external_function_parameters * @since Moodle 2.4 */ public static function get_grades_parameters() { return new external_function_parameters( array( 'assignmentids' => new external_multiple_structure( new external_value(PARAM_INT, 'assignment id'), '1 or more assignment ids', VALUE_REQUIRED), 'since' => new external_value(PARAM_INT, 'timestamp, only return records where timemodified >= since', VALUE_DEFAULT, 0) ) ); } /** * Returns grade information from assign_grades for the requested assignment ids * @param int[] $assignmentids * @param int $since only return records with timemodified >= since * @return array of grade records for each requested assignment * @since Moodle 2.4 */ public static function get_grades($assignmentids, $since = 0) { global $DB; $params = self::validate_parameters(self::get_grades_parameters(), array('assignmentids' => $assignmentids, 'since' => $since)); $assignments = array(); $warnings = array(); $requestedassignmentids = $params['assignmentids']; // Check the user is allowed to get the grades for the assignments requested. $placeholders = array(); list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED); $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ". "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids; $placeholders['modname'] = 'assign'; $cms = $DB->get_records_sql($sql, $placeholders); foreach ($cms as $cm) { try { $context = context_module::instance($cm->id); self::validate_context($context); $assign = new assign($context, null, null); $assign->require_view_grades(); } catch (Exception $e) { $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance)); $warning = array(); $warning['item'] = 'assignment'; $warning['itemid'] = $cm->instance; $warning['warningcode'] = '1'; $warning['message'] = 'No access rights in module context'; $warnings[] = $warning; } } // Create the query and populate an array of grade records from the recordset results. if (count ($requestedassignmentids) > 0) { $placeholders = array(); list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED); $sql = "SELECT ag.id, ag.assignment, ag.userid, ag.timecreated, ag.timemodified, ag.grader, ag.grade, ag.attemptnumber FROM {assign_grades} ag, {assign_submission} s WHERE s.assignment $inorequalsql AND s.userid = ag.userid AND s.latest = 1 AND s.attemptnumber = ag.attemptnumber AND ag.timemodified >= :since AND ag.assignment = s.assignment ORDER BY ag.assignment, ag.id"; $placeholders['since'] = $params['since']; $rs = $DB->get_recordset_sql($sql, $placeholders); $currentassignmentid = null; $assignment = null; foreach ($rs as $rd) { $grade = array(); $grade['id'] = $rd->id; $grade['userid'] = $rd->userid; $grade['timecreated'] = $rd->timecreated; $grade['timemodified'] = $rd->timemodified; $grade['grader'] = $rd->grader; $grade['attemptnumber'] = $rd->attemptnumber; $grade['grade'] = (string)$rd->grade; if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) { if (!is_null($assignment)) { $assignments[] = $assignment; } $assignment = array(); $assignment['assignmentid'] = $rd->assignment; $assignment['grades'] = array(); $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment)); } $assignment['grades'][] = $grade; $currentassignmentid = $rd->assignment; } if (!is_null($assignment)) { $assignments[] = $assignment; } $rs->close(); } foreach ($requestedassignmentids as $assignmentid) { $warning = array(); $warning['item'] = 'assignment'; $warning['itemid'] = $assignmentid; $warning['warningcode'] = '3'; $warning['message'] = 'No grades found'; $warnings[] = $warning; } $result = array(); $result['assignments'] = $assignments; $result['warnings'] = $warnings; return $result; } /** * Creates a grade single structure. * * @return external_single_structure a grade single structure. * @since Moodle 3.1 */ private static function get_grade_structure($required = VALUE_REQUIRED) { return new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'grade id'), 'assignment' => new external_value(PARAM_INT, 'assignment id', VALUE_OPTIONAL), 'userid' => new external_value(PARAM_INT, 'student id'), 'attemptnumber' => new external_value(PARAM_INT, 'attempt number'), 'timecreated' => new external_value(PARAM_INT, 'grade creation time'), 'timemodified' => new external_value(PARAM_INT, 'grade last modified time'), 'grader' => new external_value(PARAM_INT, 'grader, -1 if grader is hidden'), 'grade' => new external_value(PARAM_TEXT, 'grade'), 'gradefordisplay' => new external_value(PARAM_RAW, 'grade rendered into a format suitable for display', VALUE_OPTIONAL), ), 'grade information', $required ); } /** * Creates an assign_grades external_single_structure * @return external_single_structure * @since Moodle 2.4 */ private static function assign_grades() { return new external_single_structure( array ( 'assignmentid' => new external_value(PARAM_INT, 'assignment id'), 'grades' => new external_multiple_structure(self::get_grade_structure()) ) ); } /** * Describes the get_grades return value * @return external_single_structure * @since Moodle 2.4 */ public static function get_grades_returns() { return new external_single_structure( array( 'assignments' => new external_multiple_structure(self::assign_grades(), 'list of assignment grade information'), 'warnings' => new external_warnings('item is always \'assignment\'', 'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id', 'errorcode can be 3 (no grades found) or 1 (no permission to get grades)') ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.4 */ public static function get_assignments_parameters() { return new external_function_parameters( array( 'courseids' => new external_multiple_structure( new external_value(PARAM_INT, 'course id, empty for retrieving all the courses where the user is enroled in'), '0 or more course ids', VALUE_DEFAULT, array() ), 'capabilities' => new external_multiple_structure( new external_value(PARAM_CAPABILITY, 'capability'), 'list of capabilities used to filter courses', VALUE_DEFAULT, array() ), 'includenotenrolledcourses' => new external_value(PARAM_BOOL, 'whether to return courses that the user can see even if is not enroled in. This requires the parameter courseids to not be empty.', VALUE_DEFAULT, false) ) ); } /** * Returns an array of courses the user is enrolled, and for each course all of the assignments that the user can * view within that course. * * @param array $courseids An optional array of course ids. If provided only assignments within the given course * will be returned. If the user is not enrolled in or can't view a given course a warning will be generated and returned. * @param array $capabilities An array of additional capability checks you wish to be made on the course context. * @param bool $includenotenrolledcourses Wheter to return courses that the user can see even if is not enroled in. * This requires the parameter $courseids to not be empty. * @return An array of courses and warnings. * @since Moodle 2.4 */ public static function get_assignments($courseids = array(), $capabilities = array(), $includenotenrolledcourses = false) { global $USER, $DB, $CFG; $params = self::validate_parameters( self::get_assignments_parameters(), array( 'courseids' => $courseids, 'capabilities' => $capabilities, 'includenotenrolledcourses' => $includenotenrolledcourses ) ); $warnings = array(); $courses = array(); $fields = 'sortorder,shortname,fullname,timemodified'; // If the courseids list is empty, we return only the courses where the user is enrolled in. if (empty($params['courseids'])) { $courses = enrol_get_users_courses($USER->id, true, $fields); $courseids = array_keys($courses); } else if ($includenotenrolledcourses) { // In this case, we don't have to check here for enrolmnents. Maybe the user can see the course even if is not enrolled. $courseids = $params['courseids']; } else { // We need to check for enrolments. $mycourses = enrol_get_users_courses($USER->id, true, $fields); $mycourseids = array_keys($mycourses); foreach ($params['courseids'] as $courseid) { if (!in_array($courseid, $mycourseids)) { unset($courses[$courseid]); $warnings[] = array( 'item' => 'course', 'itemid' => $courseid, 'warningcode' => '2', 'message' => 'User is not enrolled or does not have requested capability' ); } else { $courses[$courseid] = $mycourses[$courseid]; } } $courseids = array_keys($courses); } foreach ($courseids as $cid) { try { $context = context_course::instance($cid); self::validate_context($context); // Check if this course was already loaded (by enrol_get_users_courses). if (!isset($courses[$cid])) { $courses[$cid] = get_course($cid); } $courses[$cid]->contextid = $context->id; } catch (Exception $e) { unset($courses[$cid]); $warnings[] = array( 'item' => 'course', 'itemid' => $cid, 'warningcode' => '1', 'message' => 'No access rights in course context '.$e->getMessage() ); continue; } if (count($params['capabilities']) > 0 && !has_all_capabilities($params['capabilities'], $context)) { unset($courses[$cid]); } } $extrafields='m.id as assignmentid, ' . 'm.course, ' . 'm.nosubmissions, ' . 'm.submissiondrafts, ' . 'm.sendnotifications, '. 'm.sendlatenotifications, ' . 'm.sendstudentnotifications, ' . 'm.duedate, ' . 'm.allowsubmissionsfromdate, '. 'm.grade, ' . 'm.gradepenalty, ' . 'm.timemodified, '. 'm.completionsubmit, ' . 'm.cutoffdate, ' . 'm.gradingduedate, ' . 'm.teamsubmission, ' . 'm.requireallteammemberssubmit, '. 'm.teamsubmissiongroupingid, ' . 'm.blindmarking, ' . 'm.hidegrader, ' . 'm.revealidentities, ' . 'm.attemptreopenmethod, '. 'm.maxattempts, ' . 'm.markingworkflow, ' . 'm.markingallocation, ' . 'm.markinganonymous, ' . 'm.requiresubmissionstatement, '. 'm.preventsubmissionnotingroup, '. 'm.intro, '. 'm.introformat,' . 'm.activity,' . 'm.activityformat,' . 'm.timelimit,' . 'm.submissionattachments'; $coursearray = array(); foreach ($courses as $id => $course) { $assignmentarray = array(); // Get a list of assignments for the course. if ($modules = get_coursemodules_in_course('assign', $courses[$id]->id, $extrafields)) { foreach ($modules as $module) { $context = context_module::instance($module->id); try { self::validate_context($context); require_capability('mod/assign:view', $context); } catch (Exception $e) { $warnings[] = array( 'item' => 'module', 'itemid' => $module->id, 'warningcode' => '1', 'message' => 'No access rights in module context' ); continue; } $assign = new assign($context, null, null); // Update assign with override information. $assign->update_effective_access($USER->id); // Get configurations for only enabled plugins. $plugins = $assign->get_submission_plugins(); $plugins = array_merge($plugins, $assign->get_feedback_plugins()); $configarray = array(); foreach ($plugins as $plugin) { if ($plugin->is_enabled() && $plugin->is_visible()) { $configrecords = $plugin->get_config_for_external(); foreach ($configrecords as $name => $value) { $configarray[] = array( 'plugin' => $plugin->get_type(), 'subtype' => $plugin->get_subtype(), 'name' => $name, 'value' => $value ); } } } $assignment = array( 'id' => $module->assignmentid, 'cmid' => $module->id, 'course' => $module->course, 'name' => \core_external\util::format_string($module->name, $context), 'nosubmissions' => $module->nosubmissions, 'submissiondrafts' => $module->submissiondrafts, 'sendnotifications' => $module->sendnotifications, 'sendlatenotifications' => $module->sendlatenotifications, 'sendstudentnotifications' => $module->sendstudentnotifications, 'duedate' => $assign->get_instance()->duedate, 'allowsubmissionsfromdate' => $assign->get_instance()->allowsubmissionsfromdate, 'grade' => $module->grade, 'gradepenalty' => $module->gradepenalty, 'timemodified' => $module->timemodified, 'completionsubmit' => $module->completionsubmit, 'cutoffdate' => $assign->get_instance()->cutoffdate, 'gradingduedate' => $assign->get_instance()->gradingduedate, 'teamsubmission' => $module->teamsubmission, 'requireallteammemberssubmit' => $module->requireallteammemberssubmit, 'teamsubmissiongroupingid' => $module->teamsubmissiongroupingid, 'blindmarking' => $module->blindmarking, 'hidegrader' => $module->hidegrader, 'revealidentities' => $module->revealidentities, 'attemptreopenmethod' => $module->attemptreopenmethod, 'maxattempts' => $module->maxattempts, 'markingworkflow' => $module->markingworkflow, 'markingallocation' => $module->markingallocation, 'markinganonymous' => $module->markinganonymous, 'requiresubmissionstatement' => $module->requiresubmissionstatement, 'preventsubmissionnotingroup' => $module->preventsubmissionnotingroup, 'timelimit' => $module->timelimit, 'submissionattachments' => $module->submissionattachments, 'configs' => $configarray ); // Return or not intro and file attachments depending on the plugin settings. if ($assign->show_intro()) { $options = array('noclean' => true); [$assignment['intro'], $assignment['introformat']] = \core_external\util::format_text( $module->intro, $module->introformat, $context, 'mod_assign', 'intro', null, $options ); $assignment['introfiles'] = external_util::get_area_files($context->id, 'mod_assign', 'intro', false, false); if ($assign->should_provide_intro_attachments($USER->id)) { $assignment['introattachments'] = external_util::get_area_files($context->id, 'mod_assign', ASSIGN_INTROATTACHMENT_FILEAREA, 0); } } if ($module->requiresubmissionstatement) { // Submission statement is required, return the submission statement value. $adminconfig = get_config('assign'); // Single submission. if (!$module->teamsubmission) { list($assignment['submissionstatement'], $assignment['submissionstatementformat']) = \core_external\util::format_text($adminconfig->submissionstatement, FORMAT_MOODLE, $context->id, 'mod_assign', '', 0); } else { // Team submission. // One user can submit for the whole team. if (!empty($adminconfig->submissionstatementteamsubmission) && !$module->requireallteammemberssubmit) { list($assignment['submissionstatement'], $assignment['submissionstatementformat']) = \core_external\util::format_text($adminconfig->submissionstatementteamsubmission, FORMAT_MOODLE, $context->id, 'mod_assign', '', 0); } else if (!empty($adminconfig->submissionstatementteamsubmissionallsubmit) && $module->requireallteammemberssubmit) { // All team members must submit. list($assignment['submissionstatement'], $assignment['submissionstatementformat']) = \core_external\util::format_text($adminconfig->submissionstatementteamsubmissionallsubmit, FORMAT_MOODLE, $context->id, 'mod_assign', '', 0); } } } if ($module->activity && $assign->submissions_open($USER->id, true)) { list($assignment['activity'], $assignment['activityformat']) = \core_external\util::format_text( $module->activity, $module->activityformat, $context, 'mod_assign', ASSIGN_ACTIVITYATTACHMENT_FILEAREA, 0 ); $assignment['activityattachments'] = external_util::get_area_files($context->id, 'mod_assign', ASSIGN_ACTIVITYATTACHMENT_FILEAREA, 0); } $assignmentarray[] = $assignment; } } $coursearray[]= array( 'id' => $courses[$id]->id, 'fullname' => \core_external\util::format_string($courses[$id]->fullname, $course->contextid), 'shortname' => \core_external\util::format_string($courses[$id]->shortname, $course->contextid), 'timemodified' => $courses[$id]->timemodified, 'assignments' => $assignmentarray ); } $result = array( 'courses' => $coursearray, 'warnings' => $warnings ); return $result; } /** * Creates an assignment external_single_structure * * @return external_single_structure * @since Moodle 2.4 */ private static function get_assignments_assignment_structure() { return new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'assignment id'), 'cmid' => new external_value(PARAM_INT, 'course module id'), 'course' => new external_value(PARAM_INT, 'course id'), 'name' => new external_value(PARAM_RAW, 'assignment name'), 'nosubmissions' => new external_value(PARAM_INT, 'no submissions'), 'submissiondrafts' => new external_value(PARAM_INT, 'submissions drafts'), 'sendnotifications' => new external_value(PARAM_INT, 'send notifications'), 'sendlatenotifications' => new external_value(PARAM_INT, 'send notifications'), 'sendstudentnotifications' => new external_value(PARAM_INT, 'send student notifications (default)'), 'duedate' => new external_value(PARAM_INT, 'assignment due date'), 'allowsubmissionsfromdate' => new external_value(PARAM_INT, 'allow submissions from date'), 'grade' => new external_value(PARAM_INT, 'grade type'), 'gradepenalty' => new external_value(PARAM_INT, 'if enabled, penalty will be applied to late submissions'), 'timemodified' => new external_value(PARAM_INT, 'last time assignment was modified'), 'completionsubmit' => new external_value(PARAM_INT, 'if enabled, set activity as complete following submission'), 'cutoffdate' => new external_value(PARAM_INT, 'date after which submission is not accepted without an extension'), 'gradingduedate' => new external_value(PARAM_INT, 'the expected date for marking the submissions'), 'teamsubmission' => new external_value(PARAM_INT, 'if enabled, students submit as a team'), 'requireallteammemberssubmit' => new external_value(PARAM_INT, 'if enabled, all team members must submit'), 'teamsubmissiongroupingid' => new external_value(PARAM_INT, 'the grouping id for the team submission groups'), 'blindmarking' => new external_value(PARAM_INT, 'if enabled, hide identities until reveal identities actioned'), 'hidegrader' => new external_value(PARAM_INT, 'If enabled, hide grader to student'), 'revealidentities' => new external_value(PARAM_INT, 'show identities for a blind marking assignment'), 'attemptreopenmethod' => new external_value(PARAM_TEXT, 'method used to control opening new attempts'), 'maxattempts' => new external_value(PARAM_INT, 'maximum number of attempts allowed'), 'markingworkflow' => new external_value(PARAM_INT, 'enable marking workflow'), 'markingallocation' => new external_value(PARAM_INT, 'enable marking allocation'), 'markinganonymous' => new external_value(PARAM_INT, 'enable marking anonymous'), 'requiresubmissionstatement' => new external_value(PARAM_INT, 'student must accept submission statement'), 'preventsubmissionnotingroup' => new external_value(PARAM_INT, 'Prevent submission not in group', VALUE_OPTIONAL), 'submissionstatement' => new external_value(PARAM_RAW, 'Submission statement formatted.', VALUE_OPTIONAL), 'submissionstatementformat' => new external_format_value('submissionstatement', VALUE_OPTIONAL), 'configs' => new external_multiple_structure(self::get_assignments_config_structure(), 'configuration settings'), 'intro' => new external_value(PARAM_RAW, 'assignment intro, not allways returned because it deppends on the activity configuration', VALUE_OPTIONAL), 'introformat' => new external_format_value('intro', VALUE_OPTIONAL), 'introfiles' => new external_files('Files in the introduction text', VALUE_OPTIONAL), 'introattachments' => new external_files('intro attachments files', VALUE_OPTIONAL), 'activity' => new external_value(PARAM_RAW, 'Description of activity', VALUE_OPTIONAL), 'activityformat' => new external_format_value('activity', VALUE_OPTIONAL), 'activityattachments' => new external_files('Files from activity field', VALUE_OPTIONAL), 'timelimit' => new external_value(PARAM_INT, 'Time limit to complete assigment', VALUE_OPTIONAL), 'submissionattachments' => new external_value(PARAM_INT, 'Flag to only show files during submission', VALUE_OPTIONAL), ), 'assignment information object'); } /** * Creates an assign_plugin_config external_single_structure * * @return external_single_structure * @since Moodle 2.4 */ private static function get_assignments_config_structure() { return new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'assign_plugin_config id', VALUE_OPTIONAL), 'assignment' => new external_value(PARAM_INT, 'assignment id', VALUE_OPTIONAL), 'plugin' => new external_value(PARAM_TEXT, 'plugin'), 'subtype' => new external_value(PARAM_TEXT, 'subtype'), 'name' => new external_value(PARAM_TEXT, 'name'), 'value' => new external_value(PARAM_TEXT, 'value') ), 'assignment configuration object' ); } /** * Creates a course external_single_structure * * @return external_single_structure * @since Moodle 2.4 */ private static function get_assignments_course_structure() { return new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'course id'), 'fullname' => new external_value(PARAM_RAW, 'course full name'), 'shortname' => new external_value(PARAM_RAW, 'course short name'), 'timemodified' => new external_value(PARAM_INT, 'last time modified'), 'assignments' => new external_multiple_structure(self::get_assignments_assignment_structure(), 'assignment info') ), 'course information object' ); } /** * Describes the return value for get_assignments * * @return external_single_structure * @since Moodle 2.4 */ public static function get_assignments_returns() { return new external_single_structure( array( 'courses' => new external_multiple_structure(self::get_assignments_course_structure(), 'list of courses'), 'warnings' => new external_warnings('item can be \'course\' (errorcode 1 or 2) or \'module\' (errorcode 1)', 'When item is a course then itemid is a course id. When the item is a module then itemid is a module id', 'errorcode can be 1 (no access rights) or 2 (not enrolled or no permissions)') ) ); } /** * Return information (files and text fields) for the given plugins in the assignment. * * @param assign $assign the assignment object * @param array $assignplugins array of assignment plugins (submission or feedback) * @param stdClass $item the item object (submission or grade) * @return array an array containing the plugins returned information */ private static function get_plugins_data($assign, $assignplugins, $item) { global $CFG; $plugins = array(); $fs = get_file_storage(); foreach ($assignplugins as $assignplugin) { if (!$assignplugin->is_enabled() or !$assignplugin->is_visible()) { continue; } $plugin = array( 'name' => $assignplugin->get_name(), 'type' => $assignplugin->get_type() ); // Subtype is 'assignsubmission', type is currently 'file' or 'onlinetext'. $component = $assignplugin->get_subtype().'_'.$assignplugin->get_type(); $fileareas = $assignplugin->get_file_areas(); foreach ($fileareas as $filearea => $name) { $fileareainfo = array('area' => $filearea); $fileareainfo['files'] = external_util::get_area_files( $assign->get_context()->id, $component, $filearea, $item->id ); $plugin['fileareas'][] = $fileareainfo; } $editorfields = $assignplugin->get_editor_fields(); foreach ($editorfields as $name => $description) { $editorfieldinfo = array( 'name' => $name, 'description' => $description, 'text' => $assignplugin->get_editor_text($name, $item->id), 'format' => $assignplugin->get_editor_format($name, $item->id) ); // Now format the text. foreach ($fileareas as $filearea => $name) { list($editorfieldinfo['text'], $editorfieldinfo['format']) = \core_external\util::format_text( $editorfieldinfo['text'], $editorfieldinfo['format'], $assign->get_context(), $component, $filearea, $item->id); } $plugin['editorfields'][] = $editorfieldinfo; } $plugins[] = $plugin; } return $plugins; } /** * Describes the parameters for get_submissions * * @return external_function_parameters * @since Moodle 2.5 */ public static function get_submissions_parameters() { return new external_function_parameters( array( 'assignmentids' => new external_multiple_structure( new external_value(PARAM_INT, 'assignment id'), '1 or more assignment ids', VALUE_REQUIRED), 'status' => new external_value(PARAM_ALPHA, 'status', VALUE_DEFAULT, ''), 'since' => new external_value(PARAM_INT, 'submitted since', VALUE_DEFAULT, 0), 'before' => new external_value(PARAM_INT, 'submitted before', VALUE_DEFAULT, 0) ) ); } /** * Returns submissions for the requested assignment ids * * @param int[] $assignmentids * @param string $status only return submissions with this status * @param int $since only return submissions with timemodified >= since * @param int $before only return submissions with timemodified <= before * @return array of submissions for each requested assignment * @since Moodle 2.5 */ public static function get_submissions($assignmentids, $status = '', $since = 0, $before = 0) { global $DB, $CFG; $params = self::validate_parameters(self::get_submissions_parameters(), array('assignmentids' => $assignmentids, 'status' => $status, 'since' => $since, 'before' => $before)); $warnings = array(); $assignments = array(); // Check the user is allowed to get the submissions for the assignments requested. $placeholders = array(); list($inorequalsql, $placeholders) = $DB->get_in_or_equal($params['assignmentids'], SQL_PARAMS_NAMED); $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ". "WHERE md.name = :modname AND cm.instance ".$inorequalsql; $placeholders['modname'] = 'assign'; $cms = $DB->get_records_sql($sql, $placeholders); $assigns = array(); foreach ($cms as $cm) { try { $context = context_module::instance($cm->id); self::validate_context($context); $assign = new assign($context, null, null); $assign->require_view_grades(); $assigns[] = $assign; } catch (Exception $e) { $warnings[] = array( 'item' => 'assignment', 'itemid' => $cm->instance, 'warningcode' => '1', 'message' => 'No access rights in module context' ); } } foreach ($assigns as $assign) { $submissions = array(); $placeholders = array('assignid1' => $assign->get_instance()->id, 'assignid2' => $assign->get_instance()->id); $submissionmaxattempt = 'SELECT mxs.userid, mxs.groupid, MAX(mxs.attemptnumber) AS maxattempt FROM {assign_submission} mxs WHERE mxs.assignment = :assignid1 GROUP BY mxs.userid, mxs.groupid'; $sql = "SELECT mas.id, mas.assignment,mas.userid,". "mas.timecreated,mas.timemodified,mas.timestarted,mas.status,mas.groupid,mas.attemptnumber ". "FROM {assign_submission} mas ". "JOIN ( " . $submissionmaxattempt . " ) smx ON mas.userid = smx.userid ". "AND mas.groupid = smx.groupid ". "WHERE mas.assignment = :assignid2 AND mas.attemptnumber = smx.maxattempt"; if (!empty($params['status'])) { $placeholders['status'] = $params['status']; $sql = $sql." AND mas.status = :status"; } if (!empty($params['before'])) { $placeholders['since'] = $params['since']; $placeholders['before'] = $params['before']; $sql = $sql." AND mas.timemodified BETWEEN :since AND :before"; } else { $placeholders['since'] = $params['since']; $sql = $sql." AND mas.timemodified >= :since"; } $submissionrecords = $DB->get_records_sql($sql, $placeholders); if (!empty($submissionrecords)) { $submissionplugins = $assign->get_submission_plugins(); foreach ($submissionrecords as $submissionrecord) { $submission = array( 'id' => $submissionrecord->id, 'userid' => $submissionrecord->userid, 'timecreated' => $submissionrecord->timecreated, 'timemodified' => $submissionrecord->timemodified, 'timestarted' => $submissionrecord->timestarted, 'status' => $submissionrecord->status, 'attemptnumber' => $submissionrecord->attemptnumber, 'groupid' => $submissionrecord->groupid, 'plugins' => self::get_plugins_data($assign, $submissionplugins, $submissionrecord), 'gradingstatus' => $assign->get_grading_status($submissionrecord->userid) ); if (($assign->get_instance()->teamsubmission && $assign->can_view_group_submission($submissionrecord->groupid)) || (!$assign->get_instance()->teamsubmission && $assign->can_view_submission($submissionrecord->userid)) ) { $submissions[] = $submission; } } } else { $warnings[] = array( 'item' => 'module', 'itemid' => $assign->get_instance()->id, 'warningcode' => '3', 'message' => 'No submissions found' ); } $assignments[] = array( 'assignmentid' => $assign->get_instance()->id, 'submissions' => $submissions ); } $result = array( 'assignments' => $assignments, 'warnings' => $warnings ); return $result; } /** * Creates an assignment plugin structure. * * @return external_single_structure the plugin structure */ private static function get_plugin_structure() { return new external_single_structure( array( 'type' => new external_value(PARAM_TEXT, 'submission plugin type'), 'name' => new external_value(PARAM_TEXT, 'submission plugin name'), 'fileareas' => new external_multiple_structure( new external_single_structure( array ( 'area' => new external_value (PARAM_TEXT, 'file area'), 'files' => new external_files('files', VALUE_OPTIONAL), ) ), 'fileareas', VALUE_OPTIONAL ), 'editorfields' => new external_multiple_structure( new external_single_structure( array( 'name' => new external_value(PARAM_TEXT, 'field name'), 'description' => new external_value(PARAM_RAW, 'field description'), 'text' => new external_value (PARAM_RAW, 'field value'), 'format' => new external_format_value ('text') ) ) , 'editorfields', VALUE_OPTIONAL ) ) ); } /** * Creates a submission structure. * * @return external_single_structure the submission structure */ private static function get_submission_structure($required = VALUE_REQUIRED) { return new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'submission id'), 'userid' => new external_value(PARAM_INT, 'student id'), 'attemptnumber' => new external_value(PARAM_INT, 'attempt number'), 'timecreated' => new external_value(PARAM_INT, 'submission creation time'), 'timemodified' => new external_value(PARAM_INT, 'submission last modified time'), 'timestarted' => new external_value(PARAM_INT, 'submission start time', VALUE_OPTIONAL), 'status' => new external_value(PARAM_TEXT, 'submission status'), 'groupid' => new external_value(PARAM_INT, 'group id'), 'assignment' => new external_value(PARAM_INT, 'assignment id', VALUE_OPTIONAL), 'latest' => new external_value(PARAM_INT, 'latest attempt', VALUE_OPTIONAL), 'plugins' => new external_multiple_structure(self::get_plugin_structure(), 'plugins', VALUE_OPTIONAL), 'gradingstatus' => new external_value(PARAM_ALPHANUMEXT, 'Grading status.', VALUE_OPTIONAL), ), 'submission info', $required ); } /** * Creates an assign_submissions external_single_structure * * @return external_single_structure * @since Moodle 2.5 */ private static function get_submissions_structure() { return new external_single_structure( array ( 'assignmentid' => new external_value(PARAM_INT, 'assignment id'), 'submissions' => new external_multiple_structure(self::get_submission_structure()) ) ); } /** * Describes the get_submissions return value * * @return external_single_structure * @since Moodle 2.5 */ public static function get_submissions_returns() { return new external_single_structure( array( 'assignments' => new external_multiple_structure(self::get_submissions_structure(), 'assignment submissions'), 'warnings' => new external_warnings() ) ); } /** * Describes the parameters for set_user_flags * @return external_function_parameters * @since Moodle 2.6 */ public static function set_user_flags_parameters() { return new external_function_parameters( array( 'assignmentid' => new external_value(PARAM_INT, 'assignment id'), 'userflags' => new external_multiple_structure( new external_single_structure( array( 'userid' => new external_value(PARAM_INT, 'student id'), 'locked' => new external_value(PARAM_INT, 'locked', VALUE_OPTIONAL), 'mailed' => new external_value(PARAM_INT, 'mailed', VALUE_OPTIONAL), 'extensionduedate' => new external_value(PARAM_INT, 'extension due date', VALUE_OPTIONAL), 'workflowstate' => new external_value(PARAM_ALPHA, 'marking workflow state', VALUE_OPTIONAL), 'allocatedmarker' => new external_value(PARAM_INT, 'allocated marker', VALUE_OPTIONAL) ) ) ) ) ); } /** * Create or update user_flags records * * @param int $assignmentid the assignment for which the userflags are created or updated * @param array $userflags An array of userflags to create or update * @return array containing success or failure information for each record * @since Moodle 2.6 */ public static function set_user_flags($assignmentid, $userflags = array()) { global $CFG, $DB; $params = self::validate_parameters(self::set_user_flags_parameters(), array('assignmentid' => $assignmentid, 'userflags' => $userflags)); // Load assignment if it exists and if the user has the capability. list($assign, $course, $cm, $context) = self::validate_assign($params['assignmentid']); require_capability('mod/assign:grade', $context); $results = array(); foreach ($params['userflags'] as $userflag) { $success = true; $result = array(); $record = $assign->get_user_flags($userflag['userid'], false); if ($record) { if (isset($userflag['locked'])) { $record->locked = $userflag['locked']; } if (isset($userflag['mailed'])) { $record->mailed = $userflag['mailed']; } if (isset($userflag['extensionduedate'])) { $record->extensionduedate = $userflag['extensionduedate']; } if (isset($userflag['workflowstate'])) { $record->workflowstate = $userflag['workflowstate']; } if (isset($userflag['allocatedmarker'])) { $record->allocatedmarker = $userflag['allocatedmarker']; } if ($assign->update_user_flags($record)) { $result['id'] = $record->id; $result['userid'] = $userflag['userid']; } else { $result['id'] = $record->id; $result['userid'] = $userflag['userid']; $result['errormessage'] = 'Record created but values could not be set'; } } else { $record = $assign->get_user_flags($userflag['userid'], true); $setfields = isset($userflag['locked']) || isset($userflag['mailed']) || isset($userflag['extensionduedate']) || isset($userflag['workflowstate']) || isset($userflag['allocatedmarker']); if ($record) { if ($setfields) { if (isset($userflag['locked'])) { $record->locked = $userflag['locked']; } if (isset($userflag['mailed'])) { $record->mailed = $userflag['mailed']; } if (isset($userflag['extensionduedate'])) { $record->extensionduedate = $userflag['extensionduedate']; } if (isset($userflag['workflowstate'])) { $record->workflowstate = $userflag['workflowstate']; } if (isset($userflag['allocatedmarker'])) { $record->allocatedmarker = $userflag['allocatedmarker']; } if ($assign->update_user_flags($record)) { $result['id'] = $record->id; $result['userid'] = $userflag['userid']; } else { $result['id'] = $record->id; $result['userid'] = $userflag['userid']; $result['errormessage'] = 'Record created but values could not be set'; } } else { $result['id'] = $record->id; $result['userid'] = $userflag['userid']; } } else { $result['id'] = -1; $result['userid'] = $userflag['userid']; $result['errormessage'] = 'Record could not be created'; } } $results[] = $result; } return $results; } /** * Describes the set_user_flags return value * @return external_multiple_structure * @since Moodle 2.6 */ public static function set_user_flags_returns() { return new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'id of record if successful, -1 for failure'), 'userid' => new external_value(PARAM_INT, 'userid of record'), 'errormessage' => new external_value(PARAM_TEXT, 'Failure error message', VALUE_OPTIONAL) ) ) ); } /** * Describes the parameters for get_user_flags * @return external_function_parameters * @since Moodle 2.6 */ public static function get_user_flags_parameters() { return new external_function_parameters( array( 'assignmentids' => new external_multiple_structure( new external_value(PARAM_INT, 'assignment id'), '1 or more assignment ids', VALUE_REQUIRED) ) ); } /** * Returns user flag information from assign_user_flags for the requested assignment ids * @param int[] $assignmentids * @return array of user flag records for each requested assignment * @since Moodle 2.6 */ public static function get_user_flags($assignmentids) { global $DB; $params = self::validate_parameters(self::get_user_flags_parameters(), array('assignmentids' => $assignmentids)); $assignments = array(); $warnings = array(); $requestedassignmentids = $params['assignmentids']; // Check the user is allowed to get the user flags for the assignments requested. $placeholders = array(); list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED); $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ". "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids; $placeholders['modname'] = 'assign'; $cms = $DB->get_records_sql($sql, $placeholders); foreach ($cms as $cm) { try { $context = context_module::instance($cm->id); self::validate_context($context); require_capability('mod/assign:grade', $context); } catch (Exception $e) { $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance)); $warning = array(); $warning['item'] = 'assignment'; $warning['itemid'] = $cm->instance; $warning['warningcode'] = '1'; $warning['message'] = 'No access rights in module context'; $warnings[] = $warning; } } // Create the query and populate an array of assign_user_flags records from the recordset results. if (count ($requestedassignmentids) > 0) { $placeholders = array(); list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED); $sql = "SELECT auf.id,auf.assignment,auf.userid,auf.locked,auf.mailed,". "auf.extensionduedate,auf.workflowstate,auf.allocatedmarker ". "FROM {assign_user_flags} auf ". "WHERE auf.assignment ".$inorequalsql. " ORDER BY auf.assignment, auf.id"; $rs = $DB->get_recordset_sql($sql, $placeholders); $currentassignmentid = null; $assignment = null; foreach ($rs as $rd) { $userflag = array(); $userflag['id'] = $rd->id; $userflag['userid'] = $rd->userid; $userflag['locked'] = $rd->locked; $userflag['mailed'] = $rd->mailed; $userflag['extensionduedate'] = $rd->extensionduedate; $userflag['workflowstate'] = $rd->workflowstate; $userflag['allocatedmarker'] = $rd->allocatedmarker; if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) { if (!is_null($assignment)) { $assignments[] = $assignment; } $assignment = array(); $assignment['assignmentid'] = $rd->assignment; $assignment['userflags'] = array(); $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment)); } $assignment['userflags'][] = $userflag; $currentassignmentid = $rd->assignment; } if (!is_null($assignment)) { $assignments[] = $assignment; } $rs->close(); } foreach ($requestedassignmentids as $assignmentid) { $warning = array(); $warning['item'] = 'assignment'; $warning['itemid'] = $assignmentid; $warning['warningcode'] = '3'; $warning['message'] = 'No user flags found'; $warnings[] = $warning; } $result = array(); $result['assignments'] = $assignments; $result['warnings'] = $warnings; return $result; } /** * Creates an assign_user_flags external_single_structure * @return external_single_structure * @since Moodle 2.6 */ private static function assign_user_flags() { return new external_single_structure( array ( 'assignmentid' => new external_value(PARAM_INT, 'assignment id'), 'userflags' => new external_multiple_structure(new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'user flag id'), 'userid' => new external_value(PARAM_INT, 'student id'), 'locked' => new external_value(PARAM_INT, 'locked'), 'mailed' => new external_value(PARAM_INT, 'mailed'), 'extensionduedate' => new external_value(PARAM_INT, 'extension due date'), 'workflowstate' => new external_value(PARAM_ALPHA, 'marking workflow state', VALUE_OPTIONAL), 'allocatedmarker' => new external_value(PARAM_INT, 'allocated marker') ) ) ) ) ); } /** * Describes the get_user_flags return value * @return external_single_structure * @since Moodle 2.6 */ public static function get_user_flags_returns() { return new external_single_structure( array( 'assignments' => new external_multiple_structure(self::assign_user_flags(), 'list of assign user flag information'), 'warnings' => new external_warnings('item is always \'assignment\'', 'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id', 'errorcode can be 3 (no user flags found) or 1 (no permission to get user flags)') ) ); } /** * Describes the parameters for get_user_mappings * @return external_function_parameters * @since Moodle 2.6 */ public static function get_user_mappings_parameters() { return new external_function_parameters( array( 'assignmentids' => new external_multiple_structure( new external_value(PARAM_INT, 'assignment id'), '1 or more assignment ids', VALUE_REQUIRED) ) ); } /** * Returns user mapping information from assign_user_mapping for the requested assignment ids * @param int[] $assignmentids * @return array of user mapping records for each requested assignment * @since Moodle 2.6 */ public static function get_user_mappings($assignmentids) { global $DB; $params = self::validate_parameters(self::get_user_mappings_parameters(), array('assignmentids' => $assignmentids)); $assignments = array(); $warnings = array(); $requestedassignmentids = $params['assignmentids']; // Check the user is allowed to get the mappings for the assignments requested. $placeholders = array(); list($sqlassignmentids, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED); $sql = "SELECT cm.id, cm.instance FROM {course_modules} cm JOIN {modules} md ON md.id = cm.module ". "WHERE md.name = :modname AND cm.instance ".$sqlassignmentids; $placeholders['modname'] = 'assign'; $cms = $DB->get_records_sql($sql, $placeholders); foreach ($cms as $cm) { try { $context = context_module::instance($cm->id); self::validate_context($context); require_capability('mod/assign:revealidentities', $context); } catch (Exception $e) { $requestedassignmentids = array_diff($requestedassignmentids, array($cm->instance)); $warning = array(); $warning['item'] = 'assignment'; $warning['itemid'] = $cm->instance; $warning['warningcode'] = '1'; $warning['message'] = 'No access rights in module context'; $warnings[] = $warning; } } // Create the query and populate an array of assign_user_mapping records from the recordset results. if (count ($requestedassignmentids) > 0) { $placeholders = array(); list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED); $sql = "SELECT aum.id,aum.assignment,aum.userid ". "FROM {assign_user_mapping} aum ". "WHERE aum.assignment ".$inorequalsql. " ORDER BY aum.assignment, aum.id"; $rs = $DB->get_recordset_sql($sql, $placeholders); $currentassignmentid = null; $assignment = null; foreach ($rs as $rd) { $mapping = array(); $mapping['id'] = $rd->id; $mapping['userid'] = $rd->userid; if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) { if (!is_null($assignment)) { $assignments[] = $assignment; } $assignment = array(); $assignment['assignmentid'] = $rd->assignment; $assignment['mappings'] = array(); $requestedassignmentids = array_diff($requestedassignmentids, array($rd->assignment)); } $assignment['mappings'][] = $mapping; $currentassignmentid = $rd->assignment; } if (!is_null($assignment)) { $assignments[] = $assignment; } $rs->close(); } foreach ($requestedassignmentids as $assignmentid) { $warning = array(); $warning['item'] = 'assignment'; $warning['itemid'] = $assignmentid; $warning['warningcode'] = '3'; $warning['message'] = 'No mappings found'; $warnings[] = $warning; } $result = array(); $result['assignments'] = $assignments; $result['warnings'] = $warnings; return $result; } /** * Creates an assign_user_mappings external_single_structure * @return external_single_structure * @since Moodle 2.6 */ private static function assign_user_mappings() { return new external_single_structure( array ( 'assignmentid' => new external_value(PARAM_INT, 'assignment id'), 'mappings' => new external_multiple_structure(new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'user mapping id'), 'userid' => new external_value(PARAM_INT, 'student id') ) ) ) ) ); } /** * Describes the get_user_mappings return value * @return external_single_structure * @since Moodle 2.6 */ public static function get_user_mappings_returns() { return new external_single_structure( array( 'assignments' => new external_multiple_structure(self::assign_user_mappings(), 'list of assign user mapping data'), 'warnings' => new external_warnings('item is always \'assignment\'', 'when errorcode is 3 then itemid is an assignment id. When errorcode is 1, itemid is a course module id', 'errorcode can be 3 (no user mappings found) or 1 (no permission to get user mappings)') ) ); } /** * Describes the parameters for lock_submissions * @return external_function_parameters * @since Moodle 2.6 */ public static function lock_submissions_parameters() { return new external_function_parameters( array( 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'), 'userids' => new external_multiple_structure( new external_value(PARAM_INT, 'user id'), '1 or more user ids', VALUE_REQUIRED), ) ); } /** * Locks (prevent updates to) submissions in this assignment. * * @param int $assignmentid The id of the assignment * @param array $userids Array of user ids to lock * @return array of warnings for each submission that could not be locked. * @since Moodle 2.6 */ public static function lock_submissions($assignmentid, $userids) { global $CFG; $params = self::validate_parameters(self::lock_submissions_parameters(), array('assignmentid' => $assignmentid, 'userids' => $userids)); list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); $warnings = array(); foreach ($params['userids'] as $userid) { if (!$assignment->lock_submission($userid)) { $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid']; $warnings[] = self::generate_warning($params['assignmentid'], 'couldnotlock', $detail); } } return $warnings; } /** * Describes the return value for lock_submissions * * @return external_single_structure * @since Moodle 2.6 */ public static function lock_submissions_returns() { return new external_warnings(); } /** * Describes the parameters for revert_submissions_to_draft * @return external_function_parameters * @since Moodle 2.6 */ public static function revert_submissions_to_draft_parameters() { return new external_function_parameters( array( 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'), 'userids' => new external_multiple_structure( new external_value(PARAM_INT, 'user id'), '1 or more user ids', VALUE_REQUIRED), ) ); } /** * Reverts a list of user submissions to draft for a single assignment. * * @param int $assignmentid The id of the assignment * @param array $userids Array of user ids to revert * @return array of warnings for each submission that could not be reverted. * @since Moodle 2.6 */ public static function revert_submissions_to_draft($assignmentid, $userids) { global $CFG; $params = self::validate_parameters(self::revert_submissions_to_draft_parameters(), array('assignmentid' => $assignmentid, 'userids' => $userids)); list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); $warnings = array(); foreach ($params['userids'] as $userid) { if (!$assignment->revert_to_draft($userid)) { $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid']; $warnings[] = self::generate_warning($params['assignmentid'], 'couldnotrevert', $detail); } } return $warnings; } /** * Describes the return value for revert_submissions_to_draft * * @return external_single_structure * @since Moodle 2.6 */ public static function revert_submissions_to_draft_returns() { return new external_warnings(); } /** * Describes the parameters for unlock_submissions * @return external_function_parameters * @since Moodle 2.6 */ public static function unlock_submissions_parameters() { return new external_function_parameters( array( 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'), 'userids' => new external_multiple_structure( new external_value(PARAM_INT, 'user id'), '1 or more user ids', VALUE_REQUIRED), ) ); } /** * Locks (prevent updates to) submissions in this assignment. * * @param int $assignmentid The id of the assignment * @param array $userids Array of user ids to lock * @return array of warnings for each submission that could not be locked. * @since Moodle 2.6 */ public static function unlock_submissions($assignmentid, $userids) { global $CFG; $params = self::validate_parameters(self::unlock_submissions_parameters(), array('assignmentid' => $assignmentid, 'userids' => $userids)); list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); $warnings = array(); foreach ($params['userids'] as $userid) { if (!$assignment->unlock_submission($userid)) { $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid']; $warnings[] = self::generate_warning($params['assignmentid'], 'couldnotunlock', $detail); } } return $warnings; } /** * Describes the return value for unlock_submissions * * @return external_single_structure * @since Moodle 2.6 */ public static function unlock_submissions_returns() { return new external_warnings(); } /** * Describes the parameters for submit_grading_form webservice. * @return external_function_parameters * @since Moodle 3.1 */ public static function submit_grading_form_parameters() { return new external_function_parameters( array( 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'), 'userid' => new external_value(PARAM_INT, 'The user id the submission belongs to'), 'jsonformdata' => new external_value(PARAM_RAW, 'The data from the grading form, encoded as a json array') ) ); } /** * Submit the logged in users assignment for grading. * * @param int $assignmentid The id of the assignment * @param int $userid The id of the user the submission belongs to. * @param string $jsonformdata The data from the form, encoded as a json array. * @return array of warnings to indicate any errors. * @since Moodle 3.1 */ public static function submit_grading_form($assignmentid, $userid, $jsonformdata) { global $CFG, $USER; require_once($CFG->dirroot . '/mod/assign/locallib.php'); require_once($CFG->dirroot . '/mod/assign/gradeform.php'); $params = self::validate_parameters(self::submit_grading_form_parameters(), array( 'assignmentid' => $assignmentid, 'userid' => $userid, 'jsonformdata' => $jsonformdata )); list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); $serialiseddata = json_decode($params['jsonformdata']); $data = array(); parse_str($serialiseddata, $data); $warnings = array(); $options = array( 'userid' => $params['userid'], 'attemptnumber' => $data['attemptnumber'], 'rownum' => 0, 'gradingpanel' => true ); if (WS_SERVER) { // Assume form submission if coming from WS. $USER->ignoresesskey = true; $data['_qf__mod_assign_grade_form_'.$params['userid']] = 1; } $customdata = (object) $data; $formparams = array($assignment, $customdata, $options); // Data is injected into the form by the last param for the constructor. $mform = new mod_assign_grade_form(null, $formparams, 'post', '', null, true, $data); $validateddata = $mform->get_data(); if ($validateddata) { $assignment->save_grade($params['userid'], $validateddata); } else { $warnings[] = self::generate_warning($params['assignmentid'], 'couldnotsavegrade', 'Form validation failed.'); } return $warnings; } /** * Describes the return for submit_grading_form * @return external_function_parameters * @since Moodle 3.1 */ public static function submit_grading_form_returns() { return new external_warnings(); } /** * Describes the parameters for submit_for_grading * @return external_function_parameters * @since Moodle 2.6 */ public static function submit_for_grading_parameters() { return new external_function_parameters( array( 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'), 'acceptsubmissionstatement' => new external_value(PARAM_BOOL, 'Accept the assignment submission statement') ) ); } /** * Submit the logged in users assignment for grading. * * @param int $assignmentid The id of the assignment * @return array of warnings to indicate any errors. * @since Moodle 2.6 */ public static function submit_for_grading($assignmentid, $acceptsubmissionstatement) { global $CFG, $USER; $params = self::validate_parameters(self::submit_for_grading_parameters(), array('assignmentid' => $assignmentid, 'acceptsubmissionstatement' => $acceptsubmissionstatement)); list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); $warnings = array(); $data = new stdClass(); $data->submissionstatement = $params['acceptsubmissionstatement']; $notices = array(); if (!$assignment->submit_for_grading($data, $notices)) { $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $params['assignmentid'] . ' Notices:' . implode(', ', $notices); $warnings[] = self::generate_warning($params['assignmentid'], 'couldnotsubmitforgrading', $detail); } return $warnings; } /** * Describes the return value for submit_for_grading * * @return external_single_structure * @since Moodle 2.6 */ public static function submit_for_grading_returns() { return new external_warnings(); } /** * Describes the parameters for save_user_extensions * @return external_function_parameters * @since Moodle 2.6 */ public static function save_user_extensions_parameters() { return new external_function_parameters( array( 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'), 'userids' => new external_multiple_structure( new external_value(PARAM_INT, 'user id'), '1 or more user ids', VALUE_REQUIRED), 'dates' => new external_multiple_structure( new external_value(PARAM_INT, 'dates'), '1 or more extension dates (timestamp)', VALUE_REQUIRED), ) ); } /** * Grant extension dates to students for an assignment. * * @param int $assignmentid The id of the assignment * @param array $userids Array of user ids to grant extensions to * @param array $dates Array of extension dates * @return array of warnings for each extension date that could not be granted * @since Moodle 2.6 */ public static function save_user_extensions($assignmentid, $userids, $dates) { global $CFG; $params = self::validate_parameters(self::save_user_extensions_parameters(), array('assignmentid' => $assignmentid, 'userids' => $userids, 'dates' => $dates)); if (count($params['userids']) != count($params['dates'])) { $detail = 'Length of userids and dates parameters differ.'; $warnings[] = self::generate_warning($params['assignmentid'], 'invalidparameters', $detail); return $warnings; } list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); $warnings = array(); foreach ($params['userids'] as $idx => $userid) { $duedate = $params['dates'][$idx]; if (!$assignment->save_user_extension($userid, $duedate)) { $detail = 'User id: ' . $userid . ', Assignment id: ' . $params['assignmentid'] . ', Extension date: ' . $duedate; $warnings[] = self::generate_warning($params['assignmentid'], 'couldnotgrantextensions', $detail); } } return $warnings; } /** * Describes the return value for save_user_extensions * * @return external_single_structure * @since Moodle 2.6 */ public static function save_user_extensions_returns() { return new external_warnings(); } /** * Describes the parameters for reveal_identities * @return external_function_parameters * @since Moodle 2.6 */ public static function reveal_identities_parameters() { return new external_function_parameters( array( 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on') ) ); } /** * Reveal the identities of anonymous students to markers for a single assignment. * * @param int $assignmentid The id of the assignment * @return array of warnings to indicate any errors. * @since Moodle 2.6 */ public static function reveal_identities($assignmentid) { global $CFG, $USER; $params = self::validate_parameters(self::reveal_identities_parameters(), array('assignmentid' => $assignmentid)); list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); $warnings = array(); if (!$assignment->reveal_identities()) { $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $params['assignmentid']; $warnings[] = self::generate_warning($params['assignmentid'], 'couldnotrevealidentities', $detail); } return $warnings; } /** * Describes the return value for reveal_identities * * @return external_single_structure * @since Moodle 2.6 */ public static function reveal_identities_returns() { return new external_warnings(); } /** * Describes the parameters for save_submission * @return external_function_parameters * @since Moodle 2.6 */ public static function save_submission_parameters() { global $CFG; $instance = new assign(null, null, null); $pluginsubmissionparams = array(); foreach ($instance->get_submission_plugins() as $plugin) { if ($plugin->is_visible()) { $pluginparams = $plugin->get_external_parameters(); if (!empty($pluginparams)) { $pluginsubmissionparams = array_merge($pluginsubmissionparams, $pluginparams); } } } return new external_function_parameters( array( 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'), 'plugindata' => new external_single_structure( $pluginsubmissionparams ) ) ); } /** * Save a student submission for a single assignment * * @param int $assignmentid The id of the assignment * @param array $plugindata - The submitted data for plugins * @return array of warnings to indicate any errors * @since Moodle 2.6 */ public static function save_submission($assignmentid, $plugindata) { global $CFG, $USER; $params = self::validate_parameters(self::save_submission_parameters(), array('assignmentid' => $assignmentid, 'plugindata' => $plugindata)); list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); $notices = array(); $assignment->update_effective_access($USER->id); if (!$assignment->submissions_open($USER->id)) { $notices[] = get_string('duedatereached', 'assign'); } else { $submissiondata = (object)$params['plugindata']; $assignment->save_submission($submissiondata, $notices); } $warnings = array(); foreach ($notices as $notice) { $warnings[] = self::generate_warning($params['assignmentid'], 'couldnotsavesubmission', $notice); } return $warnings; } /** * Describes the return value for save_submission * * @return external_single_structure * @since Moodle 2.6 */ public static function save_submission_returns() { return new external_warnings(); } /** * Describes the parameters for save_grade * @return external_function_parameters * @since Moodle 2.6 */ public static function save_grade_parameters() { global $CFG; require_once("$CFG->dirroot/grade/grading/lib.php"); $instance = new assign(null, null, null); $pluginfeedbackparams = array(); foreach ($instance->get_feedback_plugins() as $plugin) { if ($plugin->is_visible()) { $pluginparams = $plugin->get_external_parameters(); if (!empty($pluginparams)) { $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams); } } } $advancedgradingdata = array(); $methods = array_keys(grading_manager::available_methods(false)); foreach ($methods as $method) { require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php'); $details = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details'); if (!empty($details)) { $items = array(); foreach ($details as $key => $value) { $value->required = VALUE_OPTIONAL; unset($value->content->keys['id']); $items[$key] = new external_multiple_structure (new external_single_structure( array( 'criterionid' => new external_value(PARAM_INT, 'criterion id'), 'fillings' => $value ) )); } $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL); } } return new external_function_parameters( array( 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'), 'userid' => new external_value(PARAM_INT, 'The student id to operate on'), 'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. Ignored if advanced grading used'), 'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'), 'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if the attempt reopen method is manual'), 'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'), 'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' . 'to all members ' . 'of the group (for group assignments).'), 'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data', VALUE_DEFAULT, array()), 'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data', VALUE_DEFAULT, array()) ) ); } /** * Save a student grade for a single assignment. * * @param int $assignmentid The id of the assignment * @param int $userid The id of the user * @param float $grade The grade (ignored if the assignment uses advanced grading) * @param int $attemptnumber The attempt number * @param bool $addattempt Allow another attempt * @param string $workflowstate New workflow state * @param bool $applytoall Apply the grade to all members of the group * @param array $plugindata Custom data used by plugins * @param array $advancedgradingdata Advanced grading data * @return null * @since Moodle 2.6 */ public static function save_grade($assignmentid, $userid, $grade, $attemptnumber, $addattempt, $workflowstate, $applytoall, $plugindata = array(), $advancedgradingdata = array()) { global $CFG, $USER; $params = self::validate_parameters(self::save_grade_parameters(), array('assignmentid' => $assignmentid, 'userid' => $userid, 'grade' => $grade, 'attemptnumber' => $attemptnumber, 'workflowstate' => $workflowstate, 'addattempt' => $addattempt, 'applytoall' => $applytoall, 'plugindata' => $plugindata, 'advancedgradingdata' => $advancedgradingdata)); list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); $gradedata = (object)$params['plugindata']; $gradedata->addattempt = $params['addattempt']; $gradedata->attemptnumber = $params['attemptnumber']; $gradedata->workflowstate = $params['workflowstate']; $gradedata->applytoall = $params['applytoall']; $gradedata->grade = $params['grade']; if (!empty($params['advancedgradingdata'])) { $advancedgrading = array(); $criteria = reset($params['advancedgradingdata']); foreach ($criteria as $key => $criterion) { $details = array(); foreach ($criterion as $value) { foreach ($value['fillings'] as $filling) { $details[$value['criterionid']] = $filling; } } $advancedgrading[$key] = $details; } $gradedata->advancedgrading = $advancedgrading; } $assignment->save_grade($params['userid'], $gradedata); return null; } /** * Describes the return value for save_grade * * @return external_single_structure * @since Moodle 2.6 */ public static function save_grade_returns() { return null; } /** * Describes the parameters for save_grades * @return external_function_parameters * @since Moodle 2.7 */ public static function save_grades_parameters() { global $CFG; require_once("$CFG->dirroot/grade/grading/lib.php"); $instance = new assign(null, null, null); $pluginfeedbackparams = array(); foreach ($instance->get_feedback_plugins() as $plugin) { if ($plugin->is_visible()) { $pluginparams = $plugin->get_external_parameters(); if (!empty($pluginparams)) { $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams); } } } $advancedgradingdata = array(); $methods = array_keys(grading_manager::available_methods(false)); foreach ($methods as $method) { require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php'); $details = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details'); if (!empty($details)) { $items = array(); foreach ($details as $key => $value) { $value->required = VALUE_OPTIONAL; unset($value->content->keys['id']); $items[$key] = new external_multiple_structure (new external_single_structure( array( 'criterionid' => new external_value(PARAM_INT, 'criterion id'), 'fillings' => $value ) )); } $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL); } } return new external_function_parameters( array( 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'), 'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' . 'to all members ' . 'of the group (for group assignments).'), 'grades' => new external_multiple_structure( new external_single_structure( array ( 'userid' => new external_value(PARAM_INT, 'The student id to operate on'), 'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. '. 'Ignored if advanced grading used'), 'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'), 'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if manual attempt reopen method'), 'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'), 'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data', VALUE_DEFAULT, array()), 'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data', VALUE_DEFAULT, array()) ) ) ) ) ); } /** * Save multiple student grades for a single assignment. * * @param int $assignmentid The id of the assignment * @param boolean $applytoall If set to true and this is a team assignment, * apply the grade to all members of the group * @param array $grades grade data for one or more students that includes * userid - The id of the student being graded * grade - The grade (ignored if the assignment uses advanced grading) * attemptnumber - The attempt number * addattempt - Allow another attempt * workflowstate - New workflow state * plugindata - Custom data used by plugins * advancedgradingdata - Optional Advanced grading data * @throws invalid_parameter_exception if multiple grades are supplied for * a team assignment that has $applytoall set to true * @return null * @since Moodle 2.7 */ public static function save_grades($assignmentid, $applytoall, $grades) { global $CFG, $USER; $params = self::validate_parameters(self::save_grades_parameters(), array('assignmentid' => $assignmentid, 'applytoall' => $applytoall, 'grades' => $grades)); list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); if ($assignment->get_instance()->teamsubmission && $params['applytoall']) { // Check that only 1 user per submission group is provided. $groupids = array(); foreach ($params['grades'] as $gradeinfo) { $group = $assignment->get_submission_group($gradeinfo['userid']); if (in_array($group->id, $groupids)) { throw new invalid_parameter_exception('Multiple grades for the same team have been supplied ' .' this is not permitted when the applytoall flag is set'); } else { $groupids[] = $group->id; } } } foreach ($params['grades'] as $gradeinfo) { $gradedata = (object)$gradeinfo['plugindata']; $gradedata->addattempt = $gradeinfo['addattempt']; $gradedata->attemptnumber = $gradeinfo['attemptnumber']; $gradedata->workflowstate = $gradeinfo['workflowstate']; $gradedata->applytoall = $params['applytoall']; $gradedata->grade = $gradeinfo['grade']; if (!empty($gradeinfo['advancedgradingdata'])) { $advancedgrading = array(); $criteria = reset($gradeinfo['advancedgradingdata']); foreach ($criteria as $key => $criterion) { $details = array(); foreach ($criterion as $value) { foreach ($value['fillings'] as $filling) { $details[$value['criterionid']] = $filling; } } $advancedgrading[$key] = $details; } $gradedata->advancedgrading = $advancedgrading; } $assignment->save_grade($gradeinfo['userid'], $gradedata); } return null; } /** * Describes the return value for save_grades * * @return external_single_structure * @since Moodle 2.7 */ public static function save_grades_returns() { return null; } /** * Describes the parameters for copy_previous_attempt * @return external_function_parameters * @since Moodle 2.6 */ public static function copy_previous_attempt_parameters() { return new external_function_parameters( array( 'assignmentid' => new external_value(PARAM_INT, 'The assignment id to operate on'), ) ); } /** * Copy a students previous attempt to a new attempt. * * @param int $assignmentid * @return array of warnings to indicate any errors. * @since Moodle 2.6 */ public static function copy_previous_attempt($assignmentid) { $params = self::validate_parameters(self::copy_previous_attempt_parameters(), array('assignmentid' => $assignmentid)); list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']); $notices = array(); $assignment->copy_previous_attempt($notices); $warnings = array(); foreach ($notices as $notice) { $warnings[] = self::generate_warning($assignmentid, 'couldnotcopyprevioussubmission', $notice); } return $warnings; } /** * Describes the return value for save_submission * * @return external_single_structure * @since Moodle 2.6 */ public static function copy_previous_attempt_returns() { return new external_warnings(); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.0 */ public static function view_grading_table_parameters() { return new external_function_parameters( array( 'assignid' => new external_value(PARAM_INT, 'assign instance id') ) ); } /** * Trigger the grading_table_viewed event. * * @param int $assignid the assign instance id * @return array of warnings and status result * @since Moodle 3.0 * @throws moodle_exception */ public static function view_grading_table($assignid) { $params = self::validate_parameters(self::view_grading_table_parameters(), array( 'assignid' => $assignid )); $warnings = array(); list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']); $assign->require_view_grades(); \mod_assign\event\grading_table_viewed::create_from_assign($assign)->trigger(); $result = array(); $result['status'] = true; $result['warnings'] = $warnings; return $result; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 3.0 */ public static function view_grading_table_returns() { return new external_single_structure([ 'status' => new external_value(PARAM_BOOL, 'status: true if success'), 'warnings' => new external_warnings() ]); } /** * Describes the parameters for view_submission_status. * * @return external_function_parameters * @since Moodle 3.1 */ public static function view_submission_status_parameters() { return new external_function_parameters ([ 'assignid' => new external_value(PARAM_INT, 'assign instance id'), ]); } /** * Trigger the submission status viewed event. * * @param int $assignid assign instance id * @return array of warnings and status result * @since Moodle 3.1 */ public static function view_submission_status($assignid) { $warnings = array(); $params = array( 'assignid' => $assignid, ); $params = self::validate_parameters(self::view_submission_status_parameters(), $params); list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']); \mod_assign\event\submission_status_viewed::create_from_assign($assign)->trigger(); $result = array(); $result['status'] = true; $result['warnings'] = $warnings; return $result; } /** * Describes the view_submission_status return value. * * @return external_single_structure * @since Moodle 3.1 */ public static function view_submission_status_returns() { return new external_single_structure( array( 'status' => new external_value(PARAM_BOOL, 'status: true if success'), 'warnings' => new external_warnings(), ) ); } /** * Describes the parameters for get_submission_status. * * @return external_function_parameters * @since Moodle 3.1 */ public static function get_submission_status_parameters() { return new external_function_parameters ( array( 'assignid' => new external_value(PARAM_INT, 'assignment instance id'), 'userid' => new external_value(PARAM_INT, 'user id (empty for current user)', VALUE_DEFAULT, 0), 'groupid' => new external_value(PARAM_INT, 'filter by users in group (used for generating the grading summary). 0 for all groups information, any other empty value will calculate currrent group.', VALUE_DEFAULT, 0), ) ); } /** * Returns information about an assignment submission status for a given user. * * @param int $assignid assignment instance id * @param int $userid user id (empty for current user) * @param int $groupid filter by users in group id (used for generating the grading summary). Use 0 for all groups information. * @return array of warnings and grading, status, feedback and previous attempts information * @since Moodle 3.1 * @throws required_capability_exception */ public static function get_submission_status($assignid, $userid = 0, $groupid = 0) { global $USER; $warnings = array(); $params = array( 'assignid' => $assignid, 'userid' => $userid, 'groupid' => $groupid, ); $params = self::validate_parameters(self::get_submission_status_parameters(), $params); list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']); // Default value for userid. if (empty($params['userid'])) { $params['userid'] = $USER->id; } $user = core_user::get_user($params['userid'], '*', MUST_EXIST); core_user::require_active_user($user); if (!$assign->can_view_submission($user->id)) { throw new required_capability_exception($context, 'mod/assign:viewgrades', 'nopermission', ''); } $assign->update_effective_access($user->id); $gradingsummary = $lastattempt = $feedback = $previousattempts = null; // Get the renderable since it contais all the info we need. if (!empty($params['groupid'])) { $groupid = $params['groupid']; // Determine is the group is visible to user. if (!groups_group_visible($groupid, $course, $cm)) { throw new moodle_exception('notingroup'); } } else { // A null group means that following functions will calculate the current group. // A groupid set to 0 means all groups. $groupid = ($params['groupid'] == 0) ? 0 : null; } if ($assign->can_view_grades($groupid)) { $gradingsummary = $assign->get_assign_grading_summary_renderable($groupid); } // Retrieve the rest of the renderable objects. if (has_capability('mod/assign:viewownsubmissionsummary', $context, $user, false)) { // The user can view the submission summary. $lastattempt = $assign->get_assign_submission_status_renderable($user, true); } $feedback = $assign->get_assign_feedback_status_renderable($user); $previousattempts = $assign->get_assign_attempt_history_renderable($user); // Now, build the result. $result = array(); // First of all, grading summary, this is suitable for teachers/managers. if ($gradingsummary) { $result['gradingsummary'] = $gradingsummary; } // Show the grader's identity if 'Hide Grader' is disabled or has the 'Show Hidden Grader' capability. $showgradername = (has_capability('mod/assign:showhiddengrader', $context) or !$assign->is_hidden_grader()); // Did we submit anything? if ($lastattempt) { $submissionplugins = $assign->get_submission_plugins(); if (empty($lastattempt->submission)) { unset($lastattempt->submission); } else { $lastattempt->submission->plugins = self::get_plugins_data($assign, $submissionplugins, $lastattempt->submission); } if (empty($lastattempt->teamsubmission)) { unset($lastattempt->teamsubmission); } else { $lastattempt->teamsubmission->plugins = self::get_plugins_data($assign, $submissionplugins, $lastattempt->teamsubmission); } // We need to change the type of some of the structures retrieved from the renderable. if (!empty($lastattempt->submissiongroup)) { $lastattempt->submissiongroup = $lastattempt->submissiongroup->id; } else { unset($lastattempt->submissiongroup); } if (!empty($lastattempt->usergroups)) { $lastattempt->usergroups = array_keys($lastattempt->usergroups); } // We cannot use array_keys here. if (!empty($lastattempt->submissiongroupmemberswhoneedtosubmit)) { $lastattempt->submissiongroupmemberswhoneedtosubmit = array_map( function($e){ return $e->id; }, $lastattempt->submissiongroupmemberswhoneedtosubmit); } // Can edit its own submission? $lastattempt->caneditowner = has_capability('mod/assign:submit', $context, $user, false) && $assign->submissions_open($user->id) && $assign->is_any_submission_plugin_enabled(); $result['lastattempt'] = $lastattempt; } // The feedback for our latest submission. if ($feedback) { if ($feedback->grade) { if (!$showgradername) { $feedback->grade->grader = -1; } $feedbackplugins = $assign->get_feedback_plugins(); $feedback->plugins = self::get_plugins_data($assign, $feedbackplugins, $feedback->grade); } else { unset($feedback->plugins); unset($feedback->grade); } $result['feedback'] = $feedback; } // Retrieve only previous attempts. if ($previousattempts and count($previousattempts->submissions) > 1) { // Don't show the last one because it is the current submission. array_pop($previousattempts->submissions); // Show newest to oldest. $previousattempts->submissions = array_reverse($previousattempts->submissions); foreach ($previousattempts->submissions as $i => $submission) { $attempt = array(); $grade = null; foreach ($previousattempts->grades as $onegrade) { if ($onegrade->attemptnumber == $submission->attemptnumber) { $grade = $onegrade; break; } } $attempt['attemptnumber'] = $submission->attemptnumber; if ($submission) { $submission->plugins = self::get_plugins_data($assign, $previousattempts->submissionplugins, $submission); $attempt['submission'] = $submission; } if ($grade) { // From object to id. if (!$showgradername) { $grade->grader = -1; } else { $grade->grader = $grade->grader->id; } $feedbackplugins = self::get_plugins_data($assign, $previousattempts->feedbackplugins, $grade); $attempt['grade'] = $grade; $attempt['feedbackplugins'] = $feedbackplugins; } $result['previousattempts'][] = $attempt; } } // Send back some assignment data as well. $instance = $assign->get_instance(); $assignmentdata = []; $attachments = []; if ($assign->should_provide_intro_attachments($user->id)) { $attachments['intro'] = external_util::get_area_files($context->id, 'mod_assign', ASSIGN_INTROATTACHMENT_FILEAREA, 0); } if ($instance->activity && ($lastattempt || $assign->submissions_open($user->id, true))) { [$assignmentdata['activity'], $assignmentdata['activityformat']] = \core_external\util::format_text( $instance->activity, $instance->activityformat, $context, 'mod_assign', ASSIGN_ACTIVITYATTACHMENT_FILEAREA, 0 ); $attachments['activity'] = external_util::get_area_files($context->id, 'mod_assign', ASSIGN_ACTIVITYATTACHMENT_FILEAREA, 0); } if (!empty($attachments)) { $assignmentdata['attachments'] = $attachments; } $result['assignmentdata'] = $assignmentdata; $result['warnings'] = $warnings; return $result; } /** * Describes the get_submission_status return value. * * @return external_single_structure * @since Moodle 3.1 */ public static function get_submission_status_returns() { return new external_single_structure( array( 'gradingsummary' => new external_single_structure( array( 'participantcount' => new external_value(PARAM_INT, 'Number of users who can submit.'), 'submissiondraftscount' => new external_value(PARAM_INT, 'Number of submissions in draft status.'), 'submissionsenabled' => new external_value(PARAM_BOOL, 'Whether submissions are enabled or not.'), 'submissionssubmittedcount' => new external_value(PARAM_INT, 'Number of submissions in submitted status.'), 'submissionsneedgradingcount' => new external_value(PARAM_INT, 'Number of submissions that need grading.'), 'warnofungroupedusers' => new external_value(PARAM_ALPHA, 'Whether we need to warn people that there are users without groups (\'warningrequired\'), warn people there are users who will submit in the default group (\'warningoptional\') or no warning (\'\').'), ), 'Grading information.', VALUE_OPTIONAL ), 'lastattempt' => new external_single_structure( array( 'submission' => self::get_submission_structure(VALUE_OPTIONAL), 'teamsubmission' => self::get_submission_structure(VALUE_OPTIONAL), 'submissiongroup' => new external_value(PARAM_INT, 'The submission group id (for group submissions only).', VALUE_OPTIONAL), 'submissiongroupmemberswhoneedtosubmit' => new external_multiple_structure( new external_value(PARAM_INT, 'USER id.'), 'List of users who still need to submit (for group submissions only).', VALUE_OPTIONAL ), 'submissionsenabled' => new external_value(PARAM_BOOL, 'Whether submissions are enabled or not.'), 'locked' => new external_value(PARAM_BOOL, 'Whether new submissions are locked.'), 'graded' => new external_value(PARAM_BOOL, 'Whether the submission is graded.'), 'canedit' => new external_value(PARAM_BOOL, 'Whether the user can edit the current submission.'), 'caneditowner' => new external_value(PARAM_BOOL, 'Whether the owner of the submission can edit it.'), 'cansubmit' => new external_value(PARAM_BOOL, 'Whether the user can submit.'), 'extensionduedate' => new external_value(PARAM_INT, 'Extension due date.'), 'timelimit' => new external_value(PARAM_INT, 'Time limit for submission.', VALUE_OPTIONAL), 'blindmarking' => new external_value(PARAM_BOOL, 'Whether blind marking is enabled.'), 'gradingstatus' => new external_value(PARAM_ALPHANUMEXT, 'Grading status.'), 'usergroups' => new external_multiple_structure( new external_value(PARAM_INT, 'Group id.'), 'User groups in the course.' ), ), 'Last attempt information.', VALUE_OPTIONAL ), 'feedback' => new external_single_structure( array( 'grade' => self::get_grade_structure(VALUE_OPTIONAL), 'gradefordisplay' => new external_value(PARAM_RAW, 'Grade rendered into a format suitable for display.'), 'gradeddate' => new external_value(PARAM_INT, 'The date the user was graded.'), 'plugins' => new external_multiple_structure(self::get_plugin_structure(), 'Plugins info.', VALUE_OPTIONAL), ), 'Feedback for the last attempt.', VALUE_OPTIONAL ), 'previousattempts' => new external_multiple_structure( new external_single_structure( array( 'attemptnumber' => new external_value(PARAM_INT, 'Attempt number.'), 'submission' => self::get_submission_structure(VALUE_OPTIONAL), 'grade' => self::get_grade_structure(VALUE_OPTIONAL), 'feedbackplugins' => new external_multiple_structure(self::get_plugin_structure(), 'Feedback info.', VALUE_OPTIONAL), ) ), 'List all the previous attempts did by the user.', VALUE_OPTIONAL ), 'assignmentdata' => new external_single_structure([ 'attachments' => new external_single_structure([ 'intro' => new external_files('Intro attachments files', VALUE_OPTIONAL), 'activity' => new external_files('Activity attachments files', VALUE_OPTIONAL), ], 'Intro and activity attachments', VALUE_OPTIONAL), 'activity' => new external_value(PARAM_RAW, 'Text of activity', VALUE_OPTIONAL), 'activityformat' => new external_format_value('activity', VALUE_OPTIONAL), ], 'Extra information about assignment', VALUE_OPTIONAL), 'warnings' => new external_warnings(), ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.1 */ public static function list_participants_parameters() { return new external_function_parameters( array( 'assignid' => new external_value(PARAM_INT, 'assign instance id'), 'groupid' => new external_value(PARAM_INT, 'group id'), 'filter' => new external_value(PARAM_RAW, 'search string to filter the results'), 'skip' => new external_value(PARAM_INT, 'number of records to skip', VALUE_DEFAULT, 0), 'limit' => new external_value(PARAM_INT, 'maximum number of records to return', VALUE_DEFAULT, 0), 'onlyids' => new external_value(PARAM_BOOL, 'Do not return all user fields', VALUE_DEFAULT, false), 'includeenrolments' => new external_value(PARAM_BOOL, 'Do return courses where the user is enrolled', VALUE_DEFAULT, true), 'tablesort' => new external_value(PARAM_BOOL, 'Apply current user table sorting preferences.', VALUE_DEFAULT, false) ) ); } /** * Retrieves the list of students to be graded for the assignment. * * @param int $assignid the assign instance id * @param int $groupid the current group id * @param string $filter search string to filter the results. * @param int $skip Number of records to skip * @param int $limit Maximum number of records to return * @param bool $onlyids Only return user ids. * @param bool $includeenrolments Return courses where the user is enrolled. * @param bool $tablesort Apply current user table sorting params from the grading table. * @return array of warnings and status result * @since Moodle 3.1 * @throws moodle_exception */ public static function list_participants($assignid, $groupid, $filter, $skip, $limit, $onlyids, $includeenrolments, $tablesort) { global $DB, $CFG; require_once($CFG->dirroot . "/mod/assign/locallib.php"); require_once($CFG->dirroot . "/user/lib.php"); require_once($CFG->libdir . '/grouplib.php'); $params = self::validate_parameters(self::list_participants_parameters(), array( 'assignid' => $assignid, 'groupid' => $groupid, 'filter' => $filter, 'skip' => $skip, 'limit' => $limit, 'onlyids' => $onlyids, 'includeenrolments' => $includeenrolments, 'tablesort' => $tablesort )); $warnings = array(); list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']); require_capability('mod/assign:view', $context); $assign->require_view_grades(); $participants = array(); $coursegroups = []; if (groups_group_visible($params['groupid'], $course, $cm)) { $participants = $assign->list_participants_with_filter_status_and_group($params['groupid'], $params['tablesort']); $coursegroups = groups_get_all_groups($course->id); } $userfields = user_get_default_fields(); if (!$params['includeenrolments']) { // Remove enrolled courses from users fields to be returned. $key = array_search('enrolledcourses', $userfields); if ($key !== false) { unset($userfields[$key]); } else { throw new moodle_exception('invaliduserfield', 'error', '', 'enrolledcourses'); } } $result = array(); $index = 0; foreach ($participants as $record) { // Preserve the fullname set by the assignment. $fullname = $record->fullname; $searchable = $fullname; $match = false; if (empty($filter)) { $match = true; } else { $filter = core_text::strtolower($filter); $value = core_text::strtolower($searchable); if (is_string($value) && (core_text::strpos($value, $filter) !== false)) { $match = true; } } if ($match) { $index++; if ($index <= $params['skip']) { continue; } if (($params['limit'] > 0) && (($index - $params['skip']) > $params['limit'])) { break; } // Now we do the expensive lookup of user details because we completed the filtering. if (!$assign->is_blind_marking() && !$params['onlyids']) { $userdetails = user_get_user_details($record, $course, $userfields); } else { $userdetails = array('id' => $record->id); } $userdetails['fullname'] = $fullname; $userdetails['submitted'] = $record->submitted; $userdetails['requiregrading'] = $record->requiregrading; $userdetails['grantedextension'] = $record->grantedextension; $userdetails['submissionstatus'] = $record->submissionstatus; if (!empty($record->groupid)) { $userdetails['groupid'] = $record->groupid; if (!empty($coursegroups[$record->groupid])) { // Format properly the group name. $group = $coursegroups[$record->groupid]; $userdetails['groupname'] = \core_external\util::format_string($group->name, $context); } } // Unique id is required for blind marking. $userdetails['recordid'] = -1; if (!empty($record->recordid)) { $userdetails['recordid'] = $record->recordid; } $result[] = $userdetails; } } return $result; } /** * Returns the description of the results of the mod_assign_external::list_participants() method. * * @return \core_external\external_description * @since Moodle 3.1 */ public static function list_participants_returns() { // Get user description. $userdesc = core_user_external::user_description(); // List unneeded properties. $unneededproperties = [ 'auth', 'confirmed', 'lang', 'calendartype', 'theme', 'timezone', 'mailformat' ]; // Remove unneeded properties for consistency with the previous version. foreach ($unneededproperties as $prop) { unset($userdesc->keys[$prop]); } // Override property attributes for consistency with the previous version. $userdesc->keys['fullname']->type = PARAM_NOTAGS; $userdesc->keys['profileimageurlsmall']->required = VALUE_OPTIONAL; $userdesc->keys['profileimageurl']->required = VALUE_OPTIONAL; $userdesc->keys['email']->desc = 'Email address'; $userdesc->keys['idnumber']->desc = 'The idnumber of the user'; $userdesc->keys['recordid'] = new external_value(PARAM_INT, 'record id'); // Define other keys. $otherkeys = [ 'groups' => new external_multiple_structure( new external_single_structure( [ 'id' => new external_value(PARAM_INT, 'group id'), 'name' => new external_value(PARAM_RAW, 'group name'), 'description' => new external_value(PARAM_RAW, 'group description'), ] ), 'user groups', VALUE_OPTIONAL ), 'roles' => new external_multiple_structure( new external_single_structure( [ 'roleid' => new external_value(PARAM_INT, 'role id'), 'name' => new external_value(PARAM_RAW, 'role name'), 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'role shortname'), 'sortorder' => new external_value(PARAM_INT, 'role sortorder') ] ), 'user roles', VALUE_OPTIONAL ), 'enrolledcourses' => new external_multiple_structure( new external_single_structure( [ 'id' => new external_value(PARAM_INT, 'Id of the course'), 'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'), 'shortname' => new external_value(PARAM_RAW, 'Shortname of the course') ] ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL ), 'submitted' => new external_value(PARAM_BOOL, 'have they submitted their assignment'), 'requiregrading' => new external_value(PARAM_BOOL, 'is their submission waiting for grading'), 'grantedextension' => new external_value(PARAM_BOOL, 'have they been granted an extension'), 'submissionstatus' => new external_value(PARAM_ALPHA, 'The submission status (new, draft, reopened or submitted). Empty when not submitted.', VALUE_OPTIONAL), 'groupid' => new external_value(PARAM_INT, 'for group assignments this is the group id', VALUE_OPTIONAL), 'groupname' => new external_value(PARAM_TEXT, 'for group assignments this is the group name', VALUE_OPTIONAL), ]; // Merge keys. $userdesc->keys = array_merge($userdesc->keys, $otherkeys); return new external_multiple_structure($userdesc); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.1 */ public static function get_participant_parameters() { return new external_function_parameters( array( 'assignid' => new external_value(PARAM_INT, 'assign instance id'), 'userid' => new external_value(PARAM_INT, 'user id'), 'embeduser' => new external_value(PARAM_BOOL, 'user id', VALUE_DEFAULT, false), ) ); } /** * Get the user participating in the given assignment. An error with code 'usernotincourse' * is thrown is the user isn't a participant of the given assignment. * * @param int $assignid the assign instance id * @param int $userid the user id * @param bool $embeduser return user details (only applicable if not blind marking) * @return array of warnings and status result * @since Moodle 3.1 * @throws moodle_exception */ public static function get_participant($assignid, $userid, $embeduser) { global $DB, $CFG; require_once($CFG->dirroot . "/mod/assign/locallib.php"); require_once($CFG->dirroot . "/user/lib.php"); require_once($CFG->libdir . '/grouplib.php'); $params = self::validate_parameters(self::get_participant_parameters(), array( 'assignid' => $assignid, 'userid' => $userid, 'embeduser' => $embeduser )); list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']); $assign->require_view_grades(); $participant = $assign->get_participant($params['userid']); // Update assign with override information. $assign->update_effective_access($params['userid']); if (!$participant) { // No participant found so we can return early. throw new moodle_exception('usernotincourse'); } $filtered = $assign->is_userid_filtered($userid); if (!$filtered) { // User is filtered out by user filters or table preferences. throw new moodle_exception('userisfilteredout'); } $return = array( 'id' => $participant->id, 'fullname' => $participant->fullname, 'submitted' => $participant->submitted, 'requiregrading' => $participant->requiregrading, 'grantedextension' => $participant->grantedextension, 'submissionstatus' => $participant->submissionstatus, 'blindmarking' => $assign->is_blind_marking(), 'allowsubmissionsfromdate' => $assign->get_instance($userid)->allowsubmissionsfromdate, 'duedate' => $assign->get_instance($userid)->duedate, 'cutoffdate' => $assign->get_instance($userid)->cutoffdate, 'duedatestr' => userdate($assign->get_instance($userid)->duedate, get_string('strftimedatetime', 'langconfig')), ); if (!empty($participant->groupid)) { $return['groupid'] = $participant->groupid; if ($group = groups_get_group($participant->groupid)) { // Format properly the group name. $return['groupname'] = \core_external\util::format_string($group->name, $context); } } // Skip the expensive lookup of user detail if we're blind marking or the caller // hasn't asked for user details to be embedded. if (!$assign->is_blind_marking() && $embeduser) { if ($userdetails = user_get_user_details($participant, $course)) { $return['user'] = $userdetails; } } return $return; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 3.1 */ public static function get_participant_returns() { $userdescription = core_user_external::user_description(); $userdescription->default = []; $userdescription->required = VALUE_OPTIONAL; return new external_single_structure(array( 'id' => new external_value(PARAM_INT, 'ID of the user'), 'fullname' => new external_value(PARAM_NOTAGS, 'The fullname of the user'), 'submitted' => new external_value(PARAM_BOOL, 'have they submitted their assignment'), 'requiregrading' => new external_value(PARAM_BOOL, 'is their submission waiting for grading'), 'grantedextension' => new external_value(PARAM_BOOL, 'have they been granted an extension'), 'blindmarking' => new external_value(PARAM_BOOL, 'is blind marking enabled for this assignment'), 'allowsubmissionsfromdate' => new external_value(PARAM_INT, 'allowsubmissionsfromdate for the user'), 'duedate' => new external_value(PARAM_INT, 'duedate for the user'), 'cutoffdate' => new external_value(PARAM_INT, 'cutoffdate for the user'), 'duedatestr' => new external_value(PARAM_TEXT, 'duedate for the user'), 'groupid' => new external_value(PARAM_INT, 'for group assignments this is the group id', VALUE_OPTIONAL), 'groupname' => new external_value(PARAM_TEXT, 'for group assignments this is the group name', VALUE_OPTIONAL), 'submissionstatus' => new external_value(PARAM_ALPHA, 'The submission status (new, draft, reopened or submitted). Empty when not submitted.', VALUE_OPTIONAL), 'user' => $userdescription, )); } /** * Describes the parameters for view_assign. * * @return external_function_parameters * @since Moodle 3.2 */ public static function view_assign_parameters() { return new external_function_parameters ( array( 'assignid' => new external_value(PARAM_INT, 'assign instance id'), ) ); } /** * Update the module completion status. * * @param int $assignid assign instance id * @return array of warnings and status result * @since Moodle 3.2 */ public static function view_assign($assignid) { $warnings = array(); $params = array( 'assignid' => $assignid, ); $params = self::validate_parameters(self::view_assign_parameters(), $params); list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']); $assign->set_module_viewed(); $result = array(); $result['status'] = true; $result['warnings'] = $warnings; return $result; } /** * Describes the view_assign return value. * * @return external_single_structure * @since Moodle 3.2 */ public static function view_assign_returns() { return new external_single_structure( array( 'status' => new external_value(PARAM_BOOL, 'status: true if success'), 'warnings' => new external_warnings(), ) ); } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/mod/forum/externallib.php000064400000307464152267121560023637 0ustar00. use mod_forum\local\exporters\post as post_exporter; use mod_forum\local\exporters\discussion as discussion_exporter; use core_external\external_api; use core_external\external_files; use core_external\external_format_value; use core_external\external_function_parameters; use core_external\external_multiple_structure; use core_external\external_single_structure; use core_external\external_value; use core_external\external_warnings; use core_external\util as external_util; /** * External forum API * * @package mod_forum * @copyright 2012 Mark Nelson * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class mod_forum_external extends external_api { /** * Describes the parameters for get_forum. * * @return external_function_parameters * @since Moodle 2.5 */ public static function get_forums_by_courses_parameters() { return new external_function_parameters ( array( 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID', VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of Course IDs', VALUE_DEFAULT, array()), ) ); } /** * Returns a list of forums in a provided list of courses, * if no list is provided all forums that the user can view * will be returned. * * @param array $courseids the course ids * @return array the forum details * @since Moodle 2.5 */ public static function get_forums_by_courses($courseids = array()) { global $CFG; require_once($CFG->dirroot . "/mod/forum/lib.php"); $params = self::validate_parameters(self::get_forums_by_courses_parameters(), array('courseids' => $courseids)); $courses = array(); if (empty($params['courseids'])) { $courses = enrol_get_my_courses(); $params['courseids'] = array_keys($courses); } // Array to store the forums to return. $arrforums = array(); $warnings = array(); // Ensure there are courseids to loop through. if (!empty($params['courseids'])) { list($courses, $warnings) = external_util::validate_courses($params['courseids'], $courses); // Get the forums in this course. This function checks users visibility permissions. $forums = get_all_instances_in_courses("forum", $courses); foreach ($forums as $forum) { $course = $courses[$forum->course]; $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id); $context = context_module::instance($cm->id); // Skip forums we are not allowed to see discussions. if (!has_capability('mod/forum:viewdiscussion', $context)) { continue; } $forum->name = \core_external\util::format_string($forum->name, $context); // Format the intro before being returning using the format setting. $options = array('noclean' => true); [$forum->intro, $forum->introformat] = \core_external\util::format_text( $forum->intro, $forum->introformat, $context, 'mod_forum', 'intro', null, $options ); $forum->introfiles = external_util::get_area_files($context->id, 'mod_forum', 'intro', false, false); $forum->lang = clean_param($forum->lang, PARAM_LANG); // Discussions count. This function does static request cache. $forum->numdiscussions = forum_count_discussions($forum, $cm, $course); $forum->cmid = $forum->coursemodule; $forum->cancreatediscussions = forum_user_can_post_discussion($forum, null, -1, $cm, $context); $forum->istracked = forum_tp_is_tracked($forum); if ($forum->istracked) { $forum->unreadpostscount = forum_tp_count_forum_unread_posts($cm, $course); } // Add the forum to the array to return. $arrforums[$forum->id] = $forum; } } return $arrforums; } /** * Describes the get_forum return value. * * @return external_single_structure * @since Moodle 2.5 */ public static function get_forums_by_courses_returns() { // This should be using helper_for_get_mods_by_courses::standard_coursemodule_elements_returns, but it is so horribly // inconsistent with all similar web serviecs in other modules that we just can't. // Also, the return type declaration is wrong, but I am not changing it now because I don't want ot break things. return new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'Forum id'), 'course' => new external_value(PARAM_INT, 'Course id'), 'type' => new external_value(PARAM_TEXT, 'The forum type'), 'name' => new external_value(PARAM_RAW, 'Forum name'), 'intro' => new external_value(PARAM_RAW, 'The forum intro'), 'introformat' => new external_format_value('intro'), 'introfiles' => new external_files('Files in the introduction text', VALUE_OPTIONAL), 'lang' => new external_value(PARAM_SAFEDIR, 'Forced activity language', VALUE_OPTIONAL), 'duedate' => new external_value(PARAM_INT, 'duedate for the user', VALUE_OPTIONAL), 'cutoffdate' => new external_value(PARAM_INT, 'cutoffdate for the user', VALUE_OPTIONAL), 'assessed' => new external_value(PARAM_INT, 'Aggregate type'), 'assesstimestart' => new external_value(PARAM_INT, 'Assess start time'), 'assesstimefinish' => new external_value(PARAM_INT, 'Assess finish time'), 'scale' => new external_value(PARAM_INT, 'Scale'), 'grade_forum' => new external_value(PARAM_INT, 'Whole forum grade'), 'grade_forum_notify' => new external_value(PARAM_INT, 'Whether to send notifications to students upon grading by default'), 'maxbytes' => new external_value(PARAM_INT, 'Maximum attachment size'), 'maxattachments' => new external_value(PARAM_INT, 'Maximum number of attachments'), 'forcesubscribe' => new external_value(PARAM_INT, 'Force users to subscribe'), 'trackingtype' => new external_value(PARAM_INT, 'Subscription mode'), 'rsstype' => new external_value(PARAM_INT, 'RSS feed for this activity'), 'rssarticles' => new external_value(PARAM_INT, 'Number of RSS recent articles'), 'timemodified' => new external_value(PARAM_INT, 'Time modified'), 'warnafter' => new external_value(PARAM_INT, 'Post threshold for warning'), 'blockafter' => new external_value(PARAM_INT, 'Post threshold for blocking'), 'blockperiod' => new external_value(PARAM_INT, 'Time period for blocking'), 'completiondiscussions' => new external_value(PARAM_INT, 'Student must create discussions'), 'completionreplies' => new external_value(PARAM_INT, 'Student must post replies'), 'completionposts' => new external_value(PARAM_INT, 'Student must post discussions or replies'), 'cmid' => new external_value(PARAM_INT, 'Course module id'), 'numdiscussions' => new external_value(PARAM_INT, 'Number of discussions in the forum', VALUE_OPTIONAL), 'cancreatediscussions' => new external_value(PARAM_BOOL, 'If the user can create discussions', VALUE_OPTIONAL), 'lockdiscussionafter' => new external_value(PARAM_INT, 'After what period a discussion is locked', VALUE_OPTIONAL), 'istracked' => new external_value(PARAM_BOOL, 'If the user is tracking the forum', VALUE_OPTIONAL), 'unreadpostscount' => new external_value(PARAM_INT, 'The number of unread posts for tracked forums', VALUE_OPTIONAL), ), 'forum' ) ); } /** * Get the forum posts in the specified discussion. * * @param int $discussionid * @param string $sortby * @param string $sortdirection * @param bool $includeinlineattachments Whether inline attachments should be included or not. * @return array */ public static function get_discussion_posts(int $discussionid, ?string $sortby, ?string $sortdirection, bool $includeinlineattachments = false) { global $USER; // Validate the parameter. $params = self::validate_parameters(self::get_discussion_posts_parameters(), [ 'discussionid' => $discussionid, 'sortby' => $sortby, 'sortdirection' => $sortdirection, ]); $warnings = []; $vaultfactory = mod_forum\local\container::get_vault_factory(); $discussionvault = $vaultfactory->get_discussion_vault(); $discussion = $discussionvault->get_from_id($params['discussionid']); $forumvault = $vaultfactory->get_forum_vault(); $forum = $forumvault->get_from_id($discussion->get_forum_id()); $context = $forum->get_context(); self::validate_context($context); $sortby = $params['sortby']; $sortdirection = $params['sortdirection']; $sortallowedvalues = ['id', 'created', 'modified']; $directionallowedvalues = ['ASC', 'DESC']; if (!in_array(strtolower($sortby), $sortallowedvalues)) { throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' . 'allowed values are: ' . implode(', ', $sortallowedvalues)); } $sortdirection = strtoupper($sortdirection); if (!in_array($sortdirection, $directionallowedvalues)) { throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' . 'allowed values are: ' . implode(',', $directionallowedvalues)); } $orderbysql = "{$sortby} {$sortdirection}"; if (!empty($sortby) && $sortby != 'id') { $orderbysql .= ", id {$sortdirection}"; } $managerfactory = mod_forum\local\container::get_manager_factory(); $capabilitymanager = $managerfactory->get_capability_manager($forum); $postvault = $vaultfactory->get_post_vault(); $posts = $postvault->get_from_discussion_id( $USER, $discussion->get_id(), $capabilitymanager->can_view_any_private_reply($USER), $orderbysql ); $builderfactory = mod_forum\local\container::get_builder_factory(); $postbuilder = $builderfactory->get_exported_posts_builder(); $legacydatamapper = mod_forum\local\container::get_legacy_data_mapper_factory(); return [ 'posts' => $postbuilder->build($USER, [$forum], [$discussion], $posts, $includeinlineattachments), 'forumid' => $discussion->get_forum_id(), 'courseid' => $discussion->get_course_id(), 'ratinginfo' => \core_rating\external\util::get_rating_info( $legacydatamapper->get_forum_data_mapper()->to_legacy_object($forum), $forum->get_context(), 'mod_forum', 'post', $legacydatamapper->get_post_data_mapper()->to_legacy_objects($posts) ), 'warnings' => $warnings, ]; } /** * Describe the post parameters. * * @return external_function_parameters */ public static function get_discussion_posts_parameters() { return new external_function_parameters ([ 'discussionid' => new external_value(PARAM_INT, 'The ID of the discussion from which to fetch posts.', VALUE_REQUIRED), 'sortby' => new external_value(PARAM_ALPHA, 'Sort by this element: id, created or modified', VALUE_DEFAULT, 'created'), 'sortdirection' => new external_value(PARAM_ALPHA, 'Sort direction: ASC or DESC', VALUE_DEFAULT, 'DESC'), 'includeinlineattachments' => new external_value(PARAM_BOOL, 'Whether inline attachments should be included or not', VALUE_DEFAULT, false), ]); } /** * Describe the post return format. * * @return external_single_structure */ public static function get_discussion_posts_returns() { return new external_single_structure([ 'posts' => new external_multiple_structure(\mod_forum\local\exporters\post::get_read_structure()), 'forumid' => new external_value(PARAM_INT, 'The forum id'), 'courseid' => new external_value(PARAM_INT, 'The forum course id'), 'ratinginfo' => \core_rating\external\util::external_ratings_structure(), 'warnings' => new external_warnings() ]); } /** * Describes the parameters for get_forum_discussions. * * @return external_function_parameters * @since Moodle 3.7 */ public static function get_forum_discussions_parameters() { return new external_function_parameters ( array( 'forumid' => new external_value(PARAM_INT, 'forum instance id', VALUE_REQUIRED), 'sortorder' => new external_value(PARAM_INT, 'sort by this element: numreplies, , created or timemodified', VALUE_DEFAULT, -1), 'page' => new external_value(PARAM_INT, 'current page', VALUE_DEFAULT, -1), 'perpage' => new external_value(PARAM_INT, 'items per page', VALUE_DEFAULT, 0), 'groupid' => new external_value(PARAM_INT, 'group id', VALUE_DEFAULT, 0), ) ); } /** * Returns a list of forum discussions optionally sorted and paginated. * * @param int $forumid the forum instance id * @param int $sortorder The sort order * @param int $page page number * @param int $perpage items per page * @param int $groupid the user course group * * * @return array the forum discussion details including warnings * @since Moodle 3.7 */ public static function get_forum_discussions(int $forumid, ?int $sortorder = -1, ?int $page = -1, ?int $perpage = 0, ?int $groupid = 0) { global $CFG, $DB, $USER; require_once($CFG->dirroot . "/mod/forum/lib.php"); $warnings = array(); $discussions = array(); $params = self::validate_parameters(self::get_forum_discussions_parameters(), array( 'forumid' => $forumid, 'sortorder' => $sortorder, 'page' => $page, 'perpage' => $perpage, 'groupid' => $groupid ) ); // Compact/extract functions are not recommended. $forumid = $params['forumid']; $sortorder = $params['sortorder']; $page = $params['page']; $perpage = $params['perpage']; $groupid = $params['groupid']; $vaultfactory = \mod_forum\local\container::get_vault_factory(); $discussionlistvault = $vaultfactory->get_discussions_in_forum_vault(); $sortallowedvalues = array( $discussionlistvault::SORTORDER_LASTPOST_DESC, $discussionlistvault::SORTORDER_LASTPOST_ASC, $discussionlistvault::SORTORDER_CREATED_DESC, $discussionlistvault::SORTORDER_CREATED_ASC, $discussionlistvault::SORTORDER_REPLIES_DESC, $discussionlistvault::SORTORDER_REPLIES_ASC ); // If sortorder not defined set a default one. if ($sortorder == -1) { $sortorder = $discussionlistvault::SORTORDER_LASTPOST_DESC; } if (!in_array($sortorder, $sortallowedvalues)) { throw new invalid_parameter_exception('Invalid value for sortorder parameter (value: ' . $sortorder . '),' . ' allowed values are: ' . implode(',', $sortallowedvalues)); } $managerfactory = \mod_forum\local\container::get_manager_factory(); $urlfactory = \mod_forum\local\container::get_url_factory(); $legacydatamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory(); $forumvault = $vaultfactory->get_forum_vault(); $forum = $forumvault->get_from_id($forumid); if (!$forum) { throw new \moodle_exception("Unable to find forum with id {$forumid}"); } $forumdatamapper = $legacydatamapperfactory->get_forum_data_mapper(); $forumrecord = $forumdatamapper->to_legacy_object($forum); $capabilitymanager = $managerfactory->get_capability_manager($forum); $course = $DB->get_record('course', array('id' => $forum->get_course_id()), '*', MUST_EXIST); $cm = get_coursemodule_from_instance('forum', $forum->get_id(), $course->id, false, MUST_EXIST); // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..). $modcontext = context_module::instance($cm->id); self::validate_context($modcontext); $canseeanyprivatereply = $capabilitymanager->can_view_any_private_reply($USER); // Check they have the view forum capability. if (!$capabilitymanager->can_view_discussions($USER)) { throw new moodle_exception('noviewdiscussionspermission', 'forum'); } $alldiscussions = mod_forum_get_discussion_summaries($forum, $USER, $groupid, $sortorder, $page, $perpage); if ($alldiscussions) { $discussionids = array_keys($alldiscussions); $postvault = $vaultfactory->get_post_vault(); $postdatamapper = $legacydatamapperfactory->get_post_data_mapper(); // Return the reply count for each discussion in a given forum. $replies = $postvault->get_reply_count_for_discussion_ids($USER, $discussionids, $canseeanyprivatereply); // Return the first post for each discussion in a given forum. $firstposts = $postvault->get_first_post_for_discussion_ids($discussionids); // Get the unreads array, this takes a forum id and returns data for all discussions. $unreads = array(); if ($cantrack = forum_tp_can_track_forums($forumrecord)) { if ($forumtracked = forum_tp_is_tracked($forumrecord)) { $unreads = $postvault->get_unread_count_for_discussion_ids($USER, $discussionids, $canseeanyprivatereply); } } $canlock = $capabilitymanager->can_manage_forum($USER); $usercontext = context_user::instance($USER->id); $ufservice = core_favourites\service_factory::get_service_for_user_context($usercontext); $canfavourite = has_capability('mod/forum:cantogglefavourite', $modcontext, $USER); foreach ($alldiscussions as $discussionsummary) { $discussion = $discussionsummary->get_discussion(); $firstpostauthor = $discussionsummary->get_first_post_author(); $latestpostauthor = $discussionsummary->get_latest_post_author(); // This function checks for qanda forums. $canviewdiscussion = $capabilitymanager->can_view_discussion($USER, $discussion); if (!$canviewdiscussion) { $warning = array(); // Function forum_get_discussions returns forum_posts ids not forum_discussions ones. $warning['item'] = 'post'; $warning['itemid'] = $discussion->get_id(); $warning['warningcode'] = '1'; $warning['message'] = 'You can\'t see this discussion'; $warnings[] = $warning; continue; } $firstpost = $firstposts[$discussion->get_first_post_id()]; $discussionobject = $postdatamapper->to_legacy_object($firstpost); // Fix up the types for these properties. $discussionobject->mailed = $discussionobject->mailed ? 1 : 0; $discussionobject->messagetrust = $discussionobject->messagetrust ? 1 : 0; $discussionobject->mailnow = $discussionobject->mailnow ? 1 : 0; $discussionobject->groupid = $discussion->get_group_id(); $discussionobject->timemodified = $discussion->get_time_modified(); $discussionobject->usermodified = $discussion->get_user_modified(); $discussionobject->timestart = $discussion->get_time_start(); $discussionobject->timeend = $discussion->get_time_end(); $discussionobject->pinned = $discussion->is_pinned(); $discussionobject->numunread = 0; if ($cantrack && $forumtracked) { if (isset($unreads[$discussion->get_id()])) { $discussionobject->numunread = (int) $unreads[$discussion->get_id()]; } } $discussionobject->numreplies = 0; if (!empty($replies[$discussion->get_id()])) { $discussionobject->numreplies = (int) $replies[$discussion->get_id()]; } $discussionobject->name = \core_external\util::format_string($discussion->get_name(), $modcontext); $discussionobject->subject = \core_external\util::format_string($discussionobject->subject, $modcontext); // Rewrite embedded images URLs. $options = array('trusted' => $discussionobject->messagetrust); list($discussionobject->message, $discussionobject->messageformat) = \core_external\util::format_text($discussionobject->message, $discussionobject->messageformat, $modcontext, 'mod_forum', 'post', $discussionobject->id, $options); // List attachments. if (!empty($discussionobject->attachment)) { $discussionobject->attachments = external_util::get_area_files($modcontext->id, 'mod_forum', 'attachment', $discussionobject->id); } $messageinlinefiles = external_util::get_area_files($modcontext->id, 'mod_forum', 'post', $discussionobject->id); if (!empty($messageinlinefiles)) { $discussionobject->messageinlinefiles = $messageinlinefiles; } $discussionobject->locked = $forum->is_discussion_locked($discussion); $discussionobject->canlock = $canlock; $discussionobject->starred = !empty($ufservice) ? $ufservice->favourite_exists('mod_forum', 'discussions', $discussion->get_id(), $modcontext) : false; $discussionobject->canreply = $capabilitymanager->can_post_in_discussion($USER, $discussion); $discussionobject->canfavourite = $canfavourite; if (forum_is_author_hidden($discussionobject, $forumrecord)) { $discussionobject->userid = null; $discussionobject->userfullname = null; $discussionobject->userpictureurl = null; $discussionobject->usermodified = null; $discussionobject->usermodifiedfullname = null; $discussionobject->usermodifiedpictureurl = null; } else { $discussionobject->userfullname = $firstpostauthor->get_full_name(); $discussionobject->userpictureurl = $urlfactory->get_author_profile_image_url($firstpostauthor, null, 2) ->out(false); $discussionobject->usermodifiedfullname = $latestpostauthor->get_full_name(); $discussionobject->usermodifiedpictureurl = $urlfactory->get_author_profile_image_url( $latestpostauthor, null, 2)->out(false); } $discussions[] = (array) $discussionobject; } } $result = array(); $result['discussions'] = $discussions; $result['warnings'] = $warnings; return $result; } /** * Describes the get_forum_discussions return value. * * @return external_single_structure * @since Moodle 3.7 */ public static function get_forum_discussions_returns() { return new external_single_structure( array( 'discussions' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'Post id'), 'name' => new external_value(PARAM_RAW, 'Discussion name'), 'groupid' => new external_value(PARAM_INT, 'Group id'), 'timemodified' => new external_value(PARAM_INT, 'Time modified'), 'usermodified' => new external_value(PARAM_INT, 'The id of the user who last modified'), 'timestart' => new external_value(PARAM_INT, 'Time discussion can start'), 'timeend' => new external_value(PARAM_INT, 'Time discussion ends'), 'discussion' => new external_value(PARAM_INT, 'Discussion id'), 'parent' => new external_value(PARAM_INT, 'Parent id'), 'userid' => new external_value(PARAM_INT, 'User who started the discussion id'), 'created' => new external_value(PARAM_INT, 'Creation time'), 'modified' => new external_value(PARAM_INT, 'Time modified'), 'mailed' => new external_value(PARAM_INT, 'Mailed?'), 'subject' => new external_value(PARAM_RAW, 'The post subject'), 'message' => new external_value(PARAM_RAW, 'The post message'), 'messageformat' => new external_format_value('message'), 'messagetrust' => new external_value(PARAM_INT, 'Can we trust?'), 'messageinlinefiles' => new external_files('post message inline files', VALUE_OPTIONAL), 'attachment' => new external_value(PARAM_RAW, 'Has attachments?'), 'attachments' => new external_files('attachments', VALUE_OPTIONAL), 'totalscore' => new external_value(PARAM_INT, 'The post message total score'), 'mailnow' => new external_value(PARAM_INT, 'Mail now?'), 'userfullname' => new external_value(PARAM_TEXT, 'Post author full name'), 'usermodifiedfullname' => new external_value(PARAM_TEXT, 'Post modifier full name'), 'userpictureurl' => new external_value(PARAM_URL, 'Post author picture.'), 'usermodifiedpictureurl' => new external_value(PARAM_URL, 'Post modifier picture.'), 'numreplies' => new external_value(PARAM_INT, 'The number of replies in the discussion'), 'numunread' => new external_value(PARAM_INT, 'The number of unread discussions.'), 'pinned' => new external_value(PARAM_BOOL, 'Is the discussion pinned'), 'locked' => new external_value(PARAM_BOOL, 'Is the discussion locked'), 'starred' => new external_value(PARAM_BOOL, 'Is the discussion starred'), 'canreply' => new external_value(PARAM_BOOL, 'Can the user reply to the discussion'), 'canlock' => new external_value(PARAM_BOOL, 'Can the user lock the discussion'), 'canfavourite' => new external_value(PARAM_BOOL, 'Can the user star the discussion'), ), 'post' ) ), 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.9 */ public static function view_forum_parameters() { return new external_function_parameters( array( 'forumid' => new external_value(PARAM_INT, 'forum instance id') ) ); } /** * Trigger the course module viewed event and update the module completion status. * * @param int $forumid the forum instance id * @return array of warnings and status result * @since Moodle 2.9 * @throws moodle_exception */ public static function view_forum($forumid) { global $DB, $CFG; require_once($CFG->dirroot . "/mod/forum/lib.php"); $params = self::validate_parameters(self::view_forum_parameters(), array( 'forumid' => $forumid )); $warnings = array(); // Request and permission validation. $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST); list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum'); $context = context_module::instance($cm->id); self::validate_context($context); require_capability('mod/forum:viewdiscussion', $context, null, true, 'noviewdiscussionspermission', 'forum'); // Call the forum/lib API. forum_view($forum, $course, $cm, $context); $result = array(); $result['status'] = true; $result['warnings'] = $warnings; return $result; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.9 */ public static function view_forum_returns() { return new external_single_structure( array( 'status' => new external_value(PARAM_BOOL, 'status: true if success'), 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.9 */ public static function view_forum_discussion_parameters() { return new external_function_parameters( array( 'discussionid' => new external_value(PARAM_INT, 'discussion id') ) ); } /** * Trigger the discussion viewed event. * * @param int $discussionid the discussion id * @return array of warnings and status result * @since Moodle 2.9 * @throws moodle_exception */ public static function view_forum_discussion($discussionid) { global $DB, $CFG, $USER; require_once($CFG->dirroot . "/mod/forum/lib.php"); $params = self::validate_parameters(self::view_forum_discussion_parameters(), array( 'discussionid' => $discussionid )); $warnings = array(); $discussion = $DB->get_record('forum_discussions', array('id' => $params['discussionid']), '*', MUST_EXIST); $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST); list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum'); // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..). $modcontext = context_module::instance($cm->id); self::validate_context($modcontext); require_capability('mod/forum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'forum'); // Call the forum/lib API. forum_discussion_view($modcontext, $forum, $discussion); // Mark as read if required. if (!$CFG->forum_usermarksread && forum_tp_is_tracked($forum)) { forum_tp_mark_discussion_read($USER, $discussion->id); } $result = array(); $result['status'] = true; $result['warnings'] = $warnings; return $result; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.9 */ public static function view_forum_discussion_returns() { return new external_single_structure( array( 'status' => new external_value(PARAM_BOOL, 'status: true if success'), 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.0 */ public static function add_discussion_post_parameters() { return new external_function_parameters( array( 'postid' => new external_value(PARAM_INT, 'the post id we are going to reply to (can be the initial discussion post'), 'subject' => new external_value(PARAM_TEXT, 'new post subject'), 'message' => new external_value(PARAM_RAW, 'new post message (html assumed if messageformat is not provided)'), 'options' => new external_multiple_structure ( new external_single_structure( array( 'name' => new external_value(PARAM_ALPHANUM, 'The allowed keys (value format) are: discussionsubscribe (bool); subscribe to the discussion?, default to true private (bool); make this reply private to the author of the parent post, default to false. inlineattachmentsid (int); the draft file area id for inline attachments attachmentsid (int); the draft file area id for attachments topreferredformat (bool); convert the message & messageformat to FORMAT_HTML, defaults to false '), 'value' => new external_value(PARAM_RAW, 'the value of the option, this param is validated in the external function.' ) ) ), 'Options', VALUE_DEFAULT, array()), 'messageformat' => new external_format_value('message', VALUE_DEFAULT) ) ); } /** * Create new posts into an existing discussion. * * @param int $postid the post id we are going to reply to * @param string $subject new post subject * @param string $message new post message (html assumed if messageformat is not provided) * @param array $options optional settings * @param string $messageformat The format of the message, defaults to FORMAT_HTML for BC * @return array of warnings and the new post id * @since Moodle 3.0 * @throws moodle_exception */ public static function add_discussion_post($postid, $subject, $message, $options = array(), $messageformat = FORMAT_HTML) { global $CFG, $USER; require_once($CFG->dirroot . "/mod/forum/lib.php"); // Get all the factories that are required. $vaultfactory = mod_forum\local\container::get_vault_factory(); $entityfactory = mod_forum\local\container::get_entity_factory(); $datamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory(); $managerfactory = mod_forum\local\container::get_manager_factory(); $discussionvault = $vaultfactory->get_discussion_vault(); $forumvault = $vaultfactory->get_forum_vault(); $discussiondatamapper = $datamapperfactory->get_discussion_data_mapper(); $forumdatamapper = $datamapperfactory->get_forum_data_mapper(); $params = self::validate_parameters(self::add_discussion_post_parameters(), array( 'postid' => $postid, 'subject' => $subject, 'message' => $message, 'options' => $options, 'messageformat' => $messageformat, ) ); $warnings = array(); if (!$parent = forum_get_post_full($params['postid'])) { throw new moodle_exception('invalidparentpostid', 'forum'); } if (!$discussion = $discussionvault->get_from_id($parent->discussion)) { throw new moodle_exception('notpartofdiscussion', 'forum'); } // Request and permission validation. $forum = $forumvault->get_from_id($discussion->get_forum_id()); $capabilitymanager = $managerfactory->get_capability_manager($forum); $course = $forum->get_course_record(); $cm = $forum->get_course_module_record(); $discussionrecord = $discussiondatamapper->to_legacy_object($discussion); $forumrecord = $forumdatamapper->to_legacy_object($forum); $context = context_module::instance($cm->id); self::validate_context($context); $coursecontext = \context_course::instance($forum->get_course_id()); $discussionsubscribe = \mod_forum\subscriptions::get_user_default_subscription($forumrecord, $coursecontext, $cm, null); // Validate options. $options = array( 'discussionsubscribe' => $discussionsubscribe, 'private' => false, 'inlineattachmentsid' => 0, 'attachmentsid' => null, 'topreferredformat' => false ); foreach ($params['options'] as $option) { $name = trim($option['name']); switch ($name) { case 'discussionsubscribe': $value = clean_param($option['value'], PARAM_BOOL); break; case 'private': $value = clean_param($option['value'], PARAM_BOOL); break; case 'inlineattachmentsid': $value = clean_param($option['value'], PARAM_INT); break; case 'attachmentsid': $value = clean_param($option['value'], PARAM_INT); // Ensure that the user has permissions to create attachments. if (!has_capability('mod/forum:createattachment', $context)) { $value = 0; } break; case 'topreferredformat': $value = clean_param($option['value'], PARAM_BOOL); break; default: throw new moodle_exception('errorinvalidparam', 'webservice', '', $name); } $options[$name] = $value; } if (!$capabilitymanager->can_post_in_discussion($USER, $discussion)) { throw new moodle_exception('nopostforum', 'forum'); } $thresholdwarning = forum_check_throttling($forumrecord, $cm); forum_check_blocking_threshold($thresholdwarning); // If we want to force a conversion to the preferred format, let's do it now. if ($options['topreferredformat']) { // We always are going to honor the preferred format. We are creating a new post. $preferredformat = editors_get_preferred_format(); // If the post is not HTML and the preferred format is HTML, convert to it. if ($params['messageformat'] != FORMAT_HTML and $preferredformat == FORMAT_HTML) { $params['message'] = format_text($params['message'], $params['messageformat'], ['filter' => false]); } $params['messageformat'] = $preferredformat; } // Create the post. $post = new stdClass(); $post->discussion = $discussion->get_id(); $post->parent = $parent->id; $post->subject = $params['subject']; $post->message = $params['message']; $post->messageformat = $params['messageformat']; $post->messagetrust = trusttext_trusted($context); $post->itemid = $options['inlineattachmentsid']; $post->attachments = $options['attachmentsid']; $post->isprivatereply = $options['private']; $post->deleted = 0; $fakemform = $post->attachments; if ($postid = forum_add_new_post($post, $fakemform)) { $post->id = $postid; // Trigger events and completion. $params = array( 'context' => $context, 'objectid' => $post->id, 'other' => array( 'discussionid' => $discussion->get_id(), 'forumid' => $forum->get_id(), 'forumtype' => $forum->get_type(), ) ); $event = \mod_forum\event\post_created::create($params); $event->add_record_snapshot('forum_posts', $post); $event->add_record_snapshot('forum_discussions', $discussionrecord); $event->trigger(); // Update completion state. $completion = new completion_info($course); if ($completion->is_enabled($cm) && ($forum->get_completion_replies() || $forum->get_completion_posts())) { $completion->update_state($cm, COMPLETION_COMPLETE); } if ($options['discussionsubscribe']) { $settings = new stdClass(); $settings->discussionsubscribe = $options['discussionsubscribe']; forum_post_subscription($settings, $forumrecord, $discussionrecord); } } else { throw new moodle_exception('couldnotadd', 'forum'); } $builderfactory = \mod_forum\local\container::get_builder_factory(); $exportedpostsbuilder = $builderfactory->get_exported_posts_builder(); $postentity = $entityfactory->get_post_from_stdClass($post); $exportedposts = $exportedpostsbuilder->build($USER, [$forum], [$discussion], [$postentity]); $exportedpost = $exportedposts[0]; $message = []; $message[] = [ 'type' => 'success', 'message' => get_string("postaddedsuccess", "forum") ]; $message[] = [ 'type' => 'success', 'message' => get_string("postaddedtimeleft", "forum", format_time($CFG->maxeditingtime)) ]; $result = array(); $result['postid'] = $postid; $result['warnings'] = $warnings; $result['post'] = $exportedpost; $result['messages'] = $message; return $result; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 3.0 */ public static function add_discussion_post_returns() { return new external_single_structure( array( 'postid' => new external_value(PARAM_INT, 'new post id'), 'warnings' => new external_warnings(), 'post' => post_exporter::get_read_structure(), 'messages' => new external_multiple_structure( new external_single_structure( array( 'type' => new external_value(PARAM_TEXT, "The classification to be used in the client side", VALUE_REQUIRED), 'message' => new external_value(PARAM_TEXT,'untranslated english message to explain the warning', VALUE_REQUIRED) ), 'Messages'), 'list of warnings', VALUE_OPTIONAL ), //'alertmessage' => new external_value(PARAM_RAW, 'Success message to be displayed to the user.'), ) ); } /** * Toggle the favouriting value for the discussion provided * * @param int $discussionid The discussion we need to favourite * @param bool $targetstate The state of the favourite value * @return array The exported discussion */ public static function toggle_favourite_state($discussionid, $targetstate) { global $DB, $PAGE, $USER; $params = self::validate_parameters(self::toggle_favourite_state_parameters(), [ 'discussionid' => $discussionid, 'targetstate' => $targetstate ]); $vaultfactory = mod_forum\local\container::get_vault_factory(); // Get the discussion vault and the corresponding discussion entity. $discussionvault = $vaultfactory->get_discussion_vault(); $discussion = $discussionvault->get_from_id($params['discussionid']); $forumvault = $vaultfactory->get_forum_vault(); $forum = $forumvault->get_from_id($discussion->get_forum_id()); $forumcontext = $forum->get_context(); self::validate_context($forumcontext); $managerfactory = mod_forum\local\container::get_manager_factory(); $capabilitymanager = $managerfactory->get_capability_manager($forum); // Does the user have the ability to favourite the discussion? if (!$capabilitymanager->can_favourite_discussion($USER)) { throw new moodle_exception('cannotfavourite', 'forum'); } $usercontext = context_user::instance($USER->id); $ufservice = \core_favourites\service_factory::get_service_for_user_context($usercontext); $isfavourited = $ufservice->favourite_exists('mod_forum', 'discussions', $discussion->get_id(), $forumcontext); $favouritefunction = $targetstate ? 'create_favourite' : 'delete_favourite'; if ($isfavourited != (bool) $params['targetstate']) { $ufservice->{$favouritefunction}('mod_forum', 'discussions', $discussion->get_id(), $forumcontext); } $exporterfactory = mod_forum\local\container::get_exporter_factory(); $builder = mod_forum\local\container::get_builder_factory()->get_exported_discussion_builder(); $favourited = ($builder->is_favourited($discussion, $forumcontext, $USER) ? [$discussion->get_id()] : []); $exporter = $exporterfactory->get_discussion_exporter($USER, $forum, $discussion, [], $favourited); return $exporter->export($PAGE->get_renderer('mod_forum')); } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 3.0 */ public static function toggle_favourite_state_returns() { return discussion_exporter::get_read_structure(); } /** * Defines the parameters for the toggle_favourite_state method * * @return external_function_parameters */ public static function toggle_favourite_state_parameters() { return new external_function_parameters( [ 'discussionid' => new external_value(PARAM_INT, 'The discussion to subscribe or unsubscribe'), 'targetstate' => new external_value(PARAM_BOOL, 'The target state') ] ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.0 */ public static function add_discussion_parameters() { return new external_function_parameters( array( 'forumid' => new external_value(PARAM_INT, 'Forum instance ID'), 'subject' => new external_value(PARAM_TEXT, 'New Discussion subject'), 'message' => new external_value(PARAM_RAW, 'New Discussion message (only html format allowed)'), 'groupid' => new external_value(PARAM_INT, 'The group, default to 0', VALUE_DEFAULT, 0), 'options' => new external_multiple_structure ( new external_single_structure( array( 'name' => new external_value(PARAM_ALPHANUM, 'The allowed keys (value format) are: discussionsubscribe (bool); subscribe to the discussion?, default to true discussionpinned (bool); is the discussion pinned, default to false inlineattachmentsid (int); the draft file area id for inline attachments attachmentsid (int); the draft file area id for attachments '), 'value' => new external_value(PARAM_RAW, 'The value of the option, This param is validated in the external function.' ) ) ), 'Options', VALUE_DEFAULT, array()) ) ); } /** * Add a new discussion into an existing forum. * * @param int $forumid the forum instance id * @param string $subject new discussion subject * @param string $message new discussion message (only html format allowed) * @param int $groupid the user course group * @param array $options optional settings * @return array of warnings and the new discussion id * @since Moodle 3.0 * @throws moodle_exception */ public static function add_discussion($forumid, $subject, $message, $groupid = 0, $options = array()) { global $DB, $CFG; require_once($CFG->dirroot . "/mod/forum/lib.php"); $params = self::validate_parameters(self::add_discussion_parameters(), array( 'forumid' => $forumid, 'subject' => $subject, 'message' => $message, 'groupid' => $groupid, 'options' => $options )); $warnings = array(); // Request and permission validation. $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST); list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum'); $context = context_module::instance($cm->id); self::validate_context($context); // Validate options. $options = array( 'discussionsubscribe' => true, 'discussionpinned' => false, 'inlineattachmentsid' => 0, 'attachmentsid' => null ); foreach ($params['options'] as $option) { $name = trim($option['name']); switch ($name) { case 'discussionsubscribe': $value = clean_param($option['value'], PARAM_BOOL); break; case 'discussionpinned': $value = clean_param($option['value'], PARAM_BOOL); break; case 'inlineattachmentsid': $value = clean_param($option['value'], PARAM_INT); break; case 'attachmentsid': $value = clean_param($option['value'], PARAM_INT); // Ensure that the user has permissions to create attachments. if (!has_capability('mod/forum:createattachment', $context)) { $value = 0; } break; default: throw new moodle_exception('errorinvalidparam', 'webservice', '', $name); } $options[$name] = $value; } // Normalize group. if (!groups_get_activity_groupmode($cm)) { // Groups not supported, force to -1. $groupid = -1; } else { // Check if we receive the default or and empty value for groupid, // in this case, get the group for the user in the activity. if (empty($params['groupid'])) { $groupid = groups_get_activity_group($cm); } else { // Here we rely in the group passed, forum_user_can_post_discussion will validate the group. $groupid = $params['groupid']; } } if (!forum_user_can_post_discussion($forum, $groupid, -1, $cm, $context)) { throw new moodle_exception('cannotcreatediscussion', 'forum'); } $thresholdwarning = forum_check_throttling($forum, $cm); forum_check_blocking_threshold($thresholdwarning); // Create the discussion. $discussion = new stdClass(); $discussion->course = $course->id; $discussion->forum = $forum->id; $discussion->message = $params['message']; $discussion->messageformat = FORMAT_HTML; // Force formatting for now. $discussion->messagetrust = trusttext_trusted($context); $discussion->itemid = $options['inlineattachmentsid']; $discussion->groupid = $groupid; $discussion->mailnow = 0; $discussion->subject = $params['subject']; $discussion->name = $discussion->subject; $discussion->timestart = 0; $discussion->timeend = 0; $discussion->timelocked = 0; $discussion->attachments = $options['attachmentsid']; if (has_capability('mod/forum:pindiscussions', $context) && $options['discussionpinned']) { $discussion->pinned = FORUM_DISCUSSION_PINNED; } else { $discussion->pinned = FORUM_DISCUSSION_UNPINNED; } $fakemform = $options['attachmentsid']; if ($discussionid = forum_add_discussion($discussion, $fakemform)) { $discussion->id = $discussionid; // Trigger events and completion. $params = array( 'context' => $context, 'objectid' => $discussion->id, 'other' => array( 'forumid' => $forum->id, ) ); $event = \mod_forum\event\discussion_created::create($params); $event->add_record_snapshot('forum_discussions', $discussion); $event->trigger(); $completion = new completion_info($course); if ($completion->is_enabled($cm) && ($forum->completiondiscussions || $forum->completionposts)) { $completion->update_state($cm, COMPLETION_COMPLETE); } $settings = new stdClass(); $settings->discussionsubscribe = $options['discussionsubscribe']; forum_post_subscription($settings, $forum, $discussion); } else { throw new moodle_exception('couldnotadd', 'forum'); } $result = array(); $result['discussionid'] = $discussionid; $result['warnings'] = $warnings; return $result; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 3.0 */ public static function add_discussion_returns() { return new external_single_structure( array( 'discussionid' => new external_value(PARAM_INT, 'New Discussion ID'), 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.1 */ public static function can_add_discussion_parameters() { return new external_function_parameters( array( 'forumid' => new external_value(PARAM_INT, 'Forum instance ID'), 'groupid' => new external_value(PARAM_INT, 'The group to check, default to active group. Use -1 to check if the user can post in all the groups.', VALUE_DEFAULT, null) ) ); } /** * Check if the current user can add discussions in the given forum (and optionally for the given group). * * @param int $forumid the forum instance id * @param int $groupid the group to check, default to active group. Use -1 to check if the user can post in all the groups. * @return array of warnings and the status (true if the user can add discussions) * @since Moodle 3.1 * @throws moodle_exception */ public static function can_add_discussion($forumid, $groupid = null) { global $DB, $CFG; require_once($CFG->dirroot . "/mod/forum/lib.php"); $params = self::validate_parameters(self::can_add_discussion_parameters(), array( 'forumid' => $forumid, 'groupid' => $groupid, )); $warnings = array(); // Request and permission validation. $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST); list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum'); $context = context_module::instance($cm->id); self::validate_context($context); $status = forum_user_can_post_discussion($forum, $params['groupid'], -1, $cm, $context); $result = array(); $result['status'] = $status; $result['canpindiscussions'] = has_capability('mod/forum:pindiscussions', $context); $result['cancreateattachment'] = forum_can_create_attachment($forum, $context); $result['warnings'] = $warnings; return $result; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 3.1 */ public static function can_add_discussion_returns() { return new external_single_structure( array( 'status' => new external_value(PARAM_BOOL, 'True if the user can add discussions, false otherwise.'), 'canpindiscussions' => new external_value(PARAM_BOOL, 'True if the user can pin discussions, false otherwise.', VALUE_OPTIONAL), 'cancreateattachment' => new external_value(PARAM_BOOL, 'True if the user can add attachments, false otherwise.', VALUE_OPTIONAL), 'warnings' => new external_warnings() ) ); } /** * Describes the parameters for get_forum_access_information. * * @return external_external_function_parameters * @since Moodle 3.7 */ public static function get_forum_access_information_parameters() { return new external_function_parameters ( array( 'forumid' => new external_value(PARAM_INT, 'Forum instance id.') ) ); } /** * Return access information for a given forum. * * @param int $forumid forum instance id * @return array of warnings and the access information * @since Moodle 3.7 * @throws moodle_exception */ public static function get_forum_access_information($forumid) { global $DB; $params = self::validate_parameters(self::get_forum_access_information_parameters(), array('forumid' => $forumid)); // Request and permission validation. $forum = $DB->get_record('forum', array('id' => $params['forumid']), '*', MUST_EXIST); $cm = get_coursemodule_from_instance('forum', $forum->id); $context = context_module::instance($cm->id); self::validate_context($context); $result = array(); // Return all the available capabilities. $capabilities = load_capability_def('mod_forum'); foreach ($capabilities as $capname => $capdata) { // Get fields like cansubmit so it is consistent with the access_information function implemented in other modules. $field = 'can' . str_replace('mod/forum:', '', $capname); $result[$field] = has_capability($capname, $context); } $result['warnings'] = array(); return $result; } /** * Describes the get_forum_access_information return value. * * @return external_single_structure * @since Moodle 3.7 */ public static function get_forum_access_information_returns() { $structure = array( 'warnings' => new external_warnings() ); $capabilities = load_capability_def('mod_forum'); foreach ($capabilities as $capname => $capdata) { // Get fields like cansubmit so it is consistent with the access_information function implemented in other modules. $field = 'can' . str_replace('mod/forum:', '', $capname); $structure[$field] = new external_value(PARAM_BOOL, 'Whether the user has the capability ' . $capname . ' allowed.', VALUE_OPTIONAL); } return new external_single_structure($structure); } /** * Set the subscription state. * * @param int $forumid * @param int $discussionid * @param bool $targetstate * @return \stdClass */ public static function set_subscription_state($forumid, $discussionid, $targetstate) { global $PAGE, $USER; $params = self::validate_parameters(self::set_subscription_state_parameters(), [ 'forumid' => $forumid, 'discussionid' => $discussionid, 'targetstate' => $targetstate ]); $vaultfactory = mod_forum\local\container::get_vault_factory(); $forumvault = $vaultfactory->get_forum_vault(); $forum = $forumvault->get_from_id($params['forumid']); $coursemodule = $forum->get_course_module_record(); $context = $forum->get_context(); self::validate_context($context); $discussionvault = $vaultfactory->get_discussion_vault(); $discussion = $discussionvault->get_from_id($params['discussionid']); $legacydatamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory(); $forumrecord = $legacydatamapperfactory->get_forum_data_mapper()->to_legacy_object($forum); $discussionrecord = $legacydatamapperfactory->get_discussion_data_mapper()->to_legacy_object($discussion); if (!\mod_forum\subscriptions::is_subscribable($forumrecord)) { // Nothing to do. We won't actually output any content here though. throw new \moodle_exception('cannotsubscribe', 'mod_forum'); } $issubscribed = \mod_forum\subscriptions::is_subscribed( $USER->id, $forumrecord, $discussion->get_id(), $coursemodule ); // If the current state doesn't equal the desired state then update the current // state to the desired state. if ($issubscribed != (bool) $params['targetstate']) { if ($params['targetstate']) { \mod_forum\subscriptions::subscribe_user_to_discussion($USER->id, $discussionrecord, $context); } else { \mod_forum\subscriptions::unsubscribe_user_from_discussion($USER->id, $discussionrecord, $context); } } $exporterfactory = mod_forum\local\container::get_exporter_factory(); $exporter = $exporterfactory->get_discussion_exporter($USER, $forum, $discussion); return $exporter->export($PAGE->get_renderer('mod_forum')); } /** * Returns description of method parameters. * * @return external_function_parameters */ public static function set_subscription_state_parameters() { return new external_function_parameters( [ 'forumid' => new external_value(PARAM_INT, 'Forum that the discussion is in'), 'discussionid' => new external_value(PARAM_INT, 'The discussion to subscribe or unsubscribe'), 'targetstate' => new external_value(PARAM_BOOL, 'The target state') ] ); } /** * Returns description of method result value. * * @return \core_external\external_description */ public static function set_subscription_state_returns() { return discussion_exporter::get_read_structure(); } /** * Set the lock state. * * @param int $forumid * @param int $discussionid * @param string $targetstate * @return \stdClass */ public static function set_lock_state($forumid, $discussionid, $targetstate) { global $DB, $PAGE, $USER; $params = self::validate_parameters(self::set_lock_state_parameters(), [ 'forumid' => $forumid, 'discussionid' => $discussionid, 'targetstate' => $targetstate ]); $vaultfactory = mod_forum\local\container::get_vault_factory(); $forumvault = $vaultfactory->get_forum_vault(); $forum = $forumvault->get_from_id($params['forumid']); $managerfactory = mod_forum\local\container::get_manager_factory(); $capabilitymanager = $managerfactory->get_capability_manager($forum); if (!$capabilitymanager->can_manage_forum($USER)) { throw new moodle_exception('errorcannotlock', 'forum'); } // If the targetstate(currentstate) is not 0 then it should be set to the current time. $lockedvalue = $targetstate ? 0 : time(); self::validate_context($forum->get_context()); $discussionvault = $vaultfactory->get_discussion_vault(); $discussion = $discussionvault->get_from_id($params['discussionid']); // If the current state doesn't equal the desired state then update the current. // state to the desired state. $discussion->toggle_locked_state($lockedvalue); $response = $discussionvault->update_discussion($discussion); $discussion = !$response ? $response : $discussion; // Trigger an event. $params = [ 'context' => $forum->get_context(), 'objectid' => $discussion->get_id(), 'other' => ['forumid' => $forum->get_id(), 'status' => $lockedvalue ? 'locked' : 'unlocked'], ]; $event = \mod_forum\event\discussion_lock_updated::create($params); $discussionrecord = $DB->get_record('forum_discussions', ['id' => $discussion->get_id()]); $event->add_record_snapshot('forum_discussions', $discussionrecord); $event->trigger(); $exporterfactory = mod_forum\local\container::get_exporter_factory(); $exporter = $exporterfactory->get_discussion_exporter($USER, $forum, $discussion); return $exporter->export($PAGE->get_renderer('mod_forum')); } /** * Returns description of method parameters. * * @return external_function_parameters */ public static function set_lock_state_parameters() { return new external_function_parameters( [ 'forumid' => new external_value(PARAM_INT, 'Forum that the discussion is in'), 'discussionid' => new external_value(PARAM_INT, 'The discussion to lock / unlock'), 'targetstate' => new external_value(PARAM_INT, 'The timestamp for the lock state') ] ); } /** * Returns description of method result value. * * @return \core_external\external_description */ public static function set_lock_state_returns() { return new external_single_structure([ 'id' => new external_value(PARAM_INT, 'The discussion we are locking.'), 'locked' => new external_value(PARAM_BOOL, 'The locked state of the discussion.'), 'times' => new external_single_structure([ 'locked' => new external_value(PARAM_INT, 'The locked time of the discussion.'), ]) ]); } /** * Set the pin state. * * @param int $discussionid * @param bool $targetstate * @return \stdClass */ public static function set_pin_state($discussionid, $targetstate) { global $PAGE, $USER; $params = self::validate_parameters(self::set_pin_state_parameters(), [ 'discussionid' => $discussionid, 'targetstate' => $targetstate, ]); $vaultfactory = mod_forum\local\container::get_vault_factory(); $managerfactory = mod_forum\local\container::get_manager_factory(); $forumvault = $vaultfactory->get_forum_vault(); $discussionvault = $vaultfactory->get_discussion_vault(); $discussion = $discussionvault->get_from_id($params['discussionid']); $forum = $forumvault->get_from_id($discussion->get_forum_id()); $capabilitymanager = $managerfactory->get_capability_manager($forum); self::validate_context($forum->get_context()); $legacydatamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory(); if (!$capabilitymanager->can_pin_discussions($USER)) { // Nothing to do. We won't actually output any content here though. throw new \moodle_exception('cannotpindiscussions', 'mod_forum'); } $discussion->set_pinned($targetstate); $discussionvault->update_discussion($discussion); $exporterfactory = mod_forum\local\container::get_exporter_factory(); $exporter = $exporterfactory->get_discussion_exporter($USER, $forum, $discussion); return $exporter->export($PAGE->get_renderer('mod_forum')); } /** * Returns description of method parameters. * * @return external_function_parameters */ public static function set_pin_state_parameters() { return new external_function_parameters( [ 'discussionid' => new external_value(PARAM_INT, 'The discussion to pin or unpin', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), 'targetstate' => new external_value(PARAM_INT, 'The target state', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), ] ); } /** * Returns description of method result value. * * @return external_single_structure */ public static function set_pin_state_returns() { return discussion_exporter::get_read_structure(); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.8 */ public static function delete_post_parameters() { return new external_function_parameters( array( 'postid' => new external_value(PARAM_INT, 'Post to be deleted. It can be a discussion topic post.'), ) ); } /** * Deletes a post or a discussion completely when the post is the discussion topic. * * @param int $postid post to be deleted, it can be a discussion topic post. * @return array of warnings and the status (true if the post/discussion was deleted) * @since Moodle 3.8 * @throws moodle_exception */ public static function delete_post($postid) { global $USER, $CFG; require_once($CFG->dirroot . "/mod/forum/lib.php"); $params = self::validate_parameters(self::delete_post_parameters(), array( 'postid' => $postid, ) ); $warnings = array(); $vaultfactory = mod_forum\local\container::get_vault_factory(); $forumvault = $vaultfactory->get_forum_vault(); $discussionvault = $vaultfactory->get_discussion_vault(); $postvault = $vaultfactory->get_post_vault(); $postentity = $postvault->get_from_id($params['postid']); if (empty($postentity)) { throw new moodle_exception('invalidpostid', 'forum'); } $discussionentity = $discussionvault->get_from_id($postentity->get_discussion_id()); if (empty($discussionentity)) { throw new moodle_exception('notpartofdiscussion', 'forum'); } $forumentity = $forumvault->get_from_id($discussionentity->get_forum_id()); if (empty($forumentity)) { throw new moodle_exception('invalidforumid', 'forum'); } $context = $forumentity->get_context(); self::validate_context($context); $managerfactory = mod_forum\local\container::get_manager_factory(); $legacydatamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory(); $capabilitymanager = $managerfactory->get_capability_manager($forumentity); $forumdatamapper = $legacydatamapperfactory->get_forum_data_mapper(); $discussiondatamapper = $legacydatamapperfactory->get_discussion_data_mapper(); $postdatamapper = $legacydatamapperfactory->get_post_data_mapper(); $replycount = $postvault->get_reply_count_for_post_id_in_discussion_id($USER, $postentity->get_id(), $discussionentity->get_id(), true); $hasreplies = $replycount > 0; $capabilitymanager->validate_delete_post($USER, $discussionentity, $postentity, $hasreplies); if (!$postentity->has_parent()) { $status = forum_delete_discussion( $discussiondatamapper->to_legacy_object($discussionentity), false, $forumentity->get_course_record(), $forumentity->get_course_module_record(), $forumdatamapper->to_legacy_object($forumentity) ); } else { $status = forum_delete_post( $postdatamapper->to_legacy_object($postentity), has_capability('mod/forum:deleteanypost', $context), $forumentity->get_course_record(), $forumentity->get_course_module_record(), $forumdatamapper->to_legacy_object($forumentity) ); } $result = array(); $result['status'] = $status; $result['warnings'] = $warnings; return $result; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 3.8 */ public static function delete_post_returns() { return new external_single_structure( array( 'status' => new external_value(PARAM_BOOL, 'True if the post/discussion was deleted, false otherwise.'), 'warnings' => new external_warnings() ) ); } /** * Get the forum posts in the specified forum instance. * * @param int $userid * @param int $cmid * @param string $sortby * @param string $sortdirection * @return array */ public static function get_discussion_posts_by_userid(int $userid, int $cmid, ?string $sortby, ?string $sortdirection) { global $USER, $DB; // Validate the parameter. $params = self::validate_parameters(self::get_discussion_posts_by_userid_parameters(), [ 'userid' => $userid, 'cmid' => $cmid, 'sortby' => $sortby, 'sortdirection' => $sortdirection, ]); $warnings = []; $user = core_user::get_user($params['userid']); $vaultfactory = mod_forum\local\container::get_vault_factory(); $forumvault = $vaultfactory->get_forum_vault(); $forum = $forumvault->get_from_course_module_id($params['cmid']); // Validate the module context. It checks everything that affects the module visibility (including groupings, etc..). self::validate_context($forum->get_context()); $sortby = $params['sortby']; $sortdirection = $params['sortdirection']; $sortallowedvalues = ['id', 'created', 'modified']; $directionallowedvalues = ['ASC', 'DESC']; if (!in_array(strtolower($sortby), $sortallowedvalues)) { throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' . 'allowed values are: ' . implode(', ', $sortallowedvalues)); } $sortdirection = strtoupper($sortdirection); if (!in_array($sortdirection, $directionallowedvalues)) { throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' . 'allowed values are: ' . implode(',', $directionallowedvalues)); } $managerfactory = mod_forum\local\container::get_manager_factory(); $capabilitymanager = $managerfactory->get_capability_manager($forum); $discussionsummariesvault = $vaultfactory->get_discussions_in_forum_vault(); $discussionsummaries = $discussionsummariesvault->get_from_forum_id( $forum->get_id(), true, null, $discussionsummariesvault::SORTORDER_CREATED_ASC, 0, 0 ); $postvault = $vaultfactory->get_post_vault(); $builderfactory = mod_forum\local\container::get_builder_factory(); $postbuilder = $builderfactory->get_exported_posts_builder(); $builtdiscussions = []; foreach ($discussionsummaries as $discussionsummary) { $discussion = $discussionsummary->get_discussion(); if (!$capabilitymanager->can_view_discussion($USER, $discussion)) { continue; } $posts = $postvault->get_posts_in_discussion_for_user_id( $discussion->get_id(), $user->id, $capabilitymanager->can_view_any_private_reply($USER), "{$sortby} {$sortdirection}" ); if (empty($posts)) { continue; } $parentids = array_filter(array_map(function($post) { return $post->has_parent() ? $post->get_parent_id() : null; }, $posts)); $parentposts = []; if ($parentids) { $parentposts = $postbuilder->build( $USER, [$forum], [$discussion], $postvault->get_from_ids(array_values($parentids)) ); } $discussionauthor = $discussionsummary->get_first_post_author(); $firstpost = $discussionsummary->get_first_post(); $builtdiscussions[] = [ 'name' => $discussion->get_name(), 'id' => $discussion->get_id(), 'timecreated' => $firstpost->get_time_created(), 'authorfullname' => $discussionauthor->get_full_name(), 'posts' => [ 'userposts' => $postbuilder->build($USER, [$forum], [$discussion], $posts), 'parentposts' => $parentposts, ], ]; } return [ 'discussions' => $builtdiscussions, 'warnings' => $warnings, ]; } /** * Describe the post parameters. * * @return external_function_parameters */ public static function get_discussion_posts_by_userid_parameters() { return new external_function_parameters ([ 'userid' => new external_value( PARAM_INT, 'The ID of the user of whom to fetch posts.', VALUE_REQUIRED), 'cmid' => new external_value( PARAM_INT, 'The ID of the module of which to fetch items.', VALUE_REQUIRED), 'sortby' => new external_value( PARAM_ALPHA, 'Sort by this element: id, created or modified', VALUE_DEFAULT, 'created'), 'sortdirection' => new external_value( PARAM_ALPHA, 'Sort direction: ASC or DESC', VALUE_DEFAULT, 'DESC') ]); } /** * Describe the post return format. * * @return external_single_structure */ public static function get_discussion_posts_by_userid_returns() { return new external_single_structure([ 'discussions' => new external_multiple_structure( new external_single_structure([ 'name' => new external_value(PARAM_RAW, 'Name of the discussion'), 'id' => new external_value(PARAM_INT, 'ID of the discussion'), 'timecreated' => new external_value(PARAM_INT, 'Timestamp of the discussion start'), 'authorfullname' => new external_value(PARAM_RAW, 'Full name of the user that started the discussion'), 'posts' => new external_single_structure([ 'userposts' => new external_multiple_structure(\mod_forum\local\exporters\post::get_read_structure()), 'parentposts' => new external_multiple_structure(\mod_forum\local\exporters\post::get_read_structure()), ]), ])), 'warnings' => new external_warnings(), ]); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.8 */ public static function get_discussion_post_parameters() { return new external_function_parameters( array( 'postid' => new external_value(PARAM_INT, 'Post to fetch.'), ) ); } /** * Get a particular discussion post. * * @param int $postid post to fetch * @return array of post and warnings (if any) * @since Moodle 3.8 * @throws moodle_exception */ public static function get_discussion_post($postid) { global $USER, $CFG; $params = self::validate_parameters(self::get_discussion_post_parameters(), array( 'postid' => $postid, )); $warnings = array(); $vaultfactory = mod_forum\local\container::get_vault_factory(); $forumvault = $vaultfactory->get_forum_vault(); $discussionvault = $vaultfactory->get_discussion_vault(); $postvault = $vaultfactory->get_post_vault(); $postentity = $postvault->get_from_id($params['postid']); if (empty($postentity)) { throw new moodle_exception('invalidpostid', 'forum'); } $discussionentity = $discussionvault->get_from_id($postentity->get_discussion_id()); if (empty($discussionentity)) { throw new moodle_exception('notpartofdiscussion', 'forum'); } $forumentity = $forumvault->get_from_id($discussionentity->get_forum_id()); if (empty($forumentity)) { throw new moodle_exception('invalidforumid', 'forum'); } self::validate_context($forumentity->get_context()); $managerfactory = mod_forum\local\container::get_manager_factory(); $capabilitymanager = $managerfactory->get_capability_manager($forumentity); if (!$capabilitymanager->can_view_post($USER, $discussionentity, $postentity)) { throw new moodle_exception('noviewdiscussionspermission', 'forum'); } $builderfactory = mod_forum\local\container::get_builder_factory(); $postbuilder = $builderfactory->get_exported_posts_builder(); $posts = $postbuilder->build($USER, [$forumentity], [$discussionentity], [$postentity]); $post = empty($posts) ? array() : reset($posts); $result = array(); $result['post'] = $post; $result['warnings'] = $warnings; return $result; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 3.8 */ public static function get_discussion_post_returns() { return new external_single_structure( array( 'post' => \mod_forum\local\exporters\post::get_read_structure(), 'warnings' => new external_warnings(), ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.8 */ public static function prepare_draft_area_for_post_parameters() { return new external_function_parameters( array( 'postid' => new external_value(PARAM_INT, 'Post to prepare the draft area for.'), 'area' => new external_value(PARAM_ALPHA, 'Area to prepare: attachment or post.'), 'draftitemid' => new external_value(PARAM_INT, 'The draft item id to use. 0 to generate one.', VALUE_DEFAULT, 0), 'filestokeep' => new external_multiple_structure( new external_single_structure( array( 'filename' => new external_value(PARAM_FILE, 'File name.'), 'filepath' => new external_value(PARAM_PATH, 'File path.'), ) ), 'Only keep these files in the draft file area. Empty for keeping all.', VALUE_DEFAULT, [] ), ) ); } /** * Prepares a draft area for editing a post. * * @param int $postid post to prepare the draft area for * @param string $area area to prepare attachment or post * @param int $draftitemid the draft item id to use. 0 to generate a new one. * @param array $filestokeep only keep these files in the draft file area. Empty for keeping all. * @return array of files in the area, the area options and the draft item id * @since Moodle 3.8 * @throws moodle_exception */ public static function prepare_draft_area_for_post($postid, $area, $draftitemid = 0, $filestokeep = []) { global $USER; $params = self::validate_parameters( self::prepare_draft_area_for_post_parameters(), array( 'postid' => $postid, 'area' => $area, 'draftitemid' => $draftitemid, 'filestokeep' => $filestokeep, ) ); $directionallowedvalues = ['ASC', 'DESC']; $allowedareas = ['attachment', 'post']; if (!in_array($params['area'], $allowedareas)) { throw new invalid_parameter_exception('Invalid value for area parameter (value: ' . $params['area'] . '),' . 'allowed values are: ' . implode(', ', $allowedareas)); } $warnings = array(); $vaultfactory = mod_forum\local\container::get_vault_factory(); $forumvault = $vaultfactory->get_forum_vault(); $discussionvault = $vaultfactory->get_discussion_vault(); $postvault = $vaultfactory->get_post_vault(); $postentity = $postvault->get_from_id($params['postid']); if (empty($postentity)) { throw new moodle_exception('invalidpostid', 'forum'); } $discussionentity = $discussionvault->get_from_id($postentity->get_discussion_id()); if (empty($discussionentity)) { throw new moodle_exception('notpartofdiscussion', 'forum'); } $forumentity = $forumvault->get_from_id($discussionentity->get_forum_id()); if (empty($forumentity)) { throw new moodle_exception('invalidforumid', 'forum'); } $context = $forumentity->get_context(); self::validate_context($context); $managerfactory = mod_forum\local\container::get_manager_factory(); $capabilitymanager = $managerfactory->get_capability_manager($forumentity); if (!$capabilitymanager->can_edit_post($USER, $discussionentity, $postentity)) { throw new moodle_exception('noviewdiscussionspermission', 'forum'); } if ($params['area'] == 'attachment') { $legacydatamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory(); $forumdatamapper = $legacydatamapperfactory->get_forum_data_mapper(); $forum = $forumdatamapper->to_legacy_object($forumentity); $areaoptions = mod_forum_post_form::attachment_options($forum); $messagetext = null; } else { $areaoptions = mod_forum_post_form::editor_options($context, $postentity->get_id()); $messagetext = $postentity->get_message(); } $draftitemid = empty($params['draftitemid']) ? 0 : $params['draftitemid']; $messagetext = file_prepare_draft_area($draftitemid, $context->id, 'mod_forum', $params['area'], $postentity->get_id(), $areaoptions, $messagetext); // Just get a structure compatible with external API. array_walk($areaoptions, function(&$item, $key) { $item = ['name' => $key, 'value' => $item]; }); // Do we need to keep only the given files? $usercontext = context_user::instance($USER->id); if (!empty($params['filestokeep'])) { $fs = get_file_storage(); if ($areafiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid)) { $filestokeep = []; foreach ($params['filestokeep'] as $ftokeep) { $filestokeep[$ftokeep['filepath']][$ftokeep['filename']] = $ftokeep; } foreach ($areafiles as $file) { if ($file->is_directory()) { continue; } if (!isset($filestokeep[$file->get_filepath()][$file->get_filename()])) { $file->delete(); // Not in the list to be kept. } } } } $result = array( 'draftitemid' => $draftitemid, 'files' => external_util::get_area_files($usercontext->id, 'user', 'draft', $draftitemid), 'areaoptions' => $areaoptions, 'messagetext' => $messagetext, 'warnings' => $warnings, ); return $result; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 3.8 */ public static function prepare_draft_area_for_post_returns() { return new external_single_structure( array( 'draftitemid' => new external_value(PARAM_INT, 'Draft item id for the file area.'), 'files' => new external_files('Draft area files.', VALUE_OPTIONAL), 'areaoptions' => new external_multiple_structure( new external_single_structure( array( 'name' => new external_value(PARAM_RAW, 'Name of option.'), 'value' => new external_value(PARAM_RAW, 'Value of option.'), ) ), 'Draft file area options.' ), 'messagetext' => new external_value(PARAM_RAW, 'Message text with URLs rewritten.'), 'warnings' => new external_warnings(), ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.8 */ public static function update_discussion_post_parameters() { return new external_function_parameters( [ 'postid' => new external_value(PARAM_INT, 'Post to be updated. It can be a discussion topic post.'), 'subject' => new external_value(PARAM_TEXT, 'Updated post subject', VALUE_DEFAULT, ''), 'message' => new external_value(PARAM_RAW, 'Updated post message (HTML assumed if messageformat is not provided)', VALUE_DEFAULT, ''), 'messageformat' => new external_format_value('message', VALUE_DEFAULT), 'options' => new external_multiple_structure ( new external_single_structure( [ 'name' => new external_value( PARAM_ALPHANUM, 'The allowed keys (value format) are: pinned (bool); (only for discussions) whether to pin this discussion or not discussionsubscribe (bool); whether to subscribe to the post or not inlineattachmentsid (int); the draft file area id for inline attachments in the text attachmentsid (int); the draft file area id for attachments' ), 'value' => new external_value(PARAM_RAW, 'The value of the option.') ] ), 'Configuration options for the post.', VALUE_DEFAULT, [] ), ] ); } /** * Updates a post or a discussion post topic. * * @param int $postid post to be updated, it can be a discussion topic post. * @param string $subject updated post subject * @param string $message updated post message (HTML assumed if messageformat is not provided) * @param int $messageformat The format of the message, defaults to FORMAT_HTML * @param array $options different configuration options for the post to be updated. * @return array of warnings and the status (true if the post/discussion was deleted) * @since Moodle 3.8 * @throws moodle_exception * @todo support more options: timed posts, groups change and tags. */ public static function update_discussion_post($postid, $subject = '', $message = '', $messageformat = FORMAT_HTML, $options = []) { global $CFG, $USER; require_once($CFG->dirroot . "/mod/forum/lib.php"); $params = self::validate_parameters(self::add_discussion_post_parameters(), [ 'postid' => $postid, 'subject' => $subject, 'message' => $message, 'options' => $options, 'messageformat' => $messageformat, ] ); $warnings = []; // Validate options. $options = []; foreach ($params['options'] as $option) { $name = trim($option['name']); switch ($name) { case 'pinned': $value = clean_param($option['value'], PARAM_BOOL); break; case 'discussionsubscribe': $value = clean_param($option['value'], PARAM_BOOL); break; case 'inlineattachmentsid': $value = clean_param($option['value'], PARAM_INT); break; case 'attachmentsid': $value = clean_param($option['value'], PARAM_INT); break; default: throw new moodle_exception('errorinvalidparam', 'webservice', '', $name); } $options[$name] = $value; } $managerfactory = mod_forum\local\container::get_manager_factory(); $vaultfactory = mod_forum\local\container::get_vault_factory(); $forumvault = $vaultfactory->get_forum_vault(); $discussionvault = $vaultfactory->get_discussion_vault(); $postvault = $vaultfactory->get_post_vault(); $legacydatamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory(); $forumdatamapper = $legacydatamapperfactory->get_forum_data_mapper(); $discussiondatamapper = $legacydatamapperfactory->get_discussion_data_mapper(); $postdatamapper = $legacydatamapperfactory->get_post_data_mapper(); $postentity = $postvault->get_from_id($params['postid']); if (empty($postentity)) { throw new moodle_exception('invalidpostid', 'forum'); } $discussionentity = $discussionvault->get_from_id($postentity->get_discussion_id()); if (empty($discussionentity)) { throw new moodle_exception('notpartofdiscussion', 'forum'); } $forumentity = $forumvault->get_from_id($discussionentity->get_forum_id()); if (empty($forumentity)) { throw new moodle_exception('invalidforumid', 'forum'); } $forum = $forumdatamapper->to_legacy_object($forumentity); $capabilitymanager = $managerfactory->get_capability_manager($forumentity); $modcontext = $forumentity->get_context(); self::validate_context($modcontext); if (!$capabilitymanager->can_edit_post($USER, $discussionentity, $postentity)) { throw new moodle_exception('cannotupdatepost', 'forum'); } // Get the original post. $updatepost = $postdatamapper->to_legacy_object($postentity); $updatepost->itemid = IGNORE_FILE_MERGE; $updatepost->attachments = IGNORE_FILE_MERGE; // Prepare the post to be updated. if ($params['subject'] !== '') { $updatepost->subject = $params['subject']; } if ($params['message'] !== '' && isset($params['messageformat'])) { $updatepost->message = $params['message']; $updatepost->messageformat = $params['messageformat']; $updatepost->messagetrust = trusttext_trusted($modcontext); // Clean message text. $updatepost = trusttext_pre_edit($updatepost, 'message', $modcontext); } if (isset($options['discussionsubscribe'])) { // No need to validate anything here, forum_post_subscription will do. $updatepost->discussionsubscribe = $options['discussionsubscribe']; } // When editing first post/discussion. if (!$postentity->has_parent()) { // Defaults for discussion topic posts. $updatepost->name = $discussionentity->get_name(); $updatepost->timestart = $discussionentity->get_time_start(); $updatepost->timeend = $discussionentity->get_time_end(); if (isset($options['pinned'])) { if ($capabilitymanager->can_pin_discussions($USER)) { // Can change pinned if we have capability. $updatepost->pinned = !empty($options['pinned']) ? FORUM_DISCUSSION_PINNED : FORUM_DISCUSSION_UNPINNED; } } } if (isset($options['inlineattachmentsid'])) { $updatepost->itemid = $options['inlineattachmentsid']; } if (isset($options['attachmentsid']) && forum_can_create_attachment($forum, $modcontext)) { $updatepost->attachments = $options['attachmentsid']; } // Update the post. $fakemform = $updatepost->id; if (forum_update_post($updatepost, $fakemform)) { $discussion = $discussiondatamapper->to_legacy_object($discussionentity); forum_trigger_post_updated_event($updatepost, $discussion, $modcontext, $forum); forum_post_subscription( $updatepost, $forum, $discussion ); $status = true; } else { $status = false; } return [ 'status' => $status, 'warnings' => $warnings, ]; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 3.8 */ public static function update_discussion_post_returns() { return new external_single_structure( [ 'status' => new external_value(PARAM_BOOL, 'True if the post/discussion was updated, false otherwise.'), 'warnings' => new external_warnings() ] ); } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/lib/externallib.php000064400000023044152267520610022462 0ustar00. /** * Support for external API * * @package core_webservice * @copyright 2009 Petr Skodak * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ use core_external\util; defined('MOODLE_INTERNAL') || die; // Please note that this file and all of the classes and functions listed below will be deprecated from Moodle 4.6. // This deprecation is delayed to aid plugin developers when maintaining plugins for multiple Moodle versions. // See MDL-76583 for further information. // If including this file for unit testing, it _must_ be run in an isolated process to prevent // any side effect upon other tests. require_phpunit_isolation(); class_alias(\core_external\external_api::class, 'external_api'); class_alias(\core_external\restricted_context_exception::class, 'restricted_context_exception'); class_alias(\core_external\external_description::class, 'external_description'); class_alias(\core_external\external_value::class, 'external_value'); class_alias(\core_external\external_format_value::class, 'external_format_value'); class_alias(\core_external\external_single_structure::class, 'external_single_structure'); class_alias(\core_external\external_multiple_structure::class, 'external_multiple_structure'); class_alias(\core_external\external_function_parameters::class, 'external_function_parameters'); class_alias(\core_external\util::class, 'external_util'); class_alias(\core_external\external_files::class, 'external_files'); class_alias(\core_external\external_warnings::class, 'external_warnings'); class_alias(\core_external\external_settings::class, 'external_settings'); /** * Generate a token * * @param string $tokentype EXTERNAL_TOKEN_EMBEDDED|EXTERNAL_TOKEN_PERMANENT * @param stdClass|int $serviceorid service linked to the token * @param int $userid user linked to the token * @param stdClass|int $contextorid * @param int $validuntil date when the token expired * @param string $iprestriction allowed ip - if 0 or empty then all ips are allowed * @return string generated token * @since Moodle 2.0 */ function external_generate_token($tokentype, $serviceorid, $userid, $contextorid, $validuntil = 0, $iprestriction = '') { if (is_numeric($serviceorid)) { $service = util::get_service_by_id($serviceorid); } else if (is_string($serviceorid)) { $service = util::get_service_by_name($serviceorid); } else { $service = $serviceorid; } if (!is_object($contextorid)) { $context = context::instance_by_id($contextorid, MUST_EXIST); } else { $context = $contextorid; } return util::generate_token( $tokentype, $service, $userid, $context, $validuntil, $iprestriction ); } /** * Create and return a session linked token. Token to be used for html embedded client apps that want to communicate * with the Moodle server through web services. The token is linked to the current session for the current page request. * It is expected this will be called in the script generating the html page that is embedding the client app and that the * returned token will be somehow passed into the client app being embedded in the page. * * @param string $servicename name of the web service. Service name as defined in db/services.php * @param int $context context within which the web service can operate. * @return string returns token id. * @since Moodle 2.0 */ function external_create_service_token($servicename, $contextid) { global $USER; return util::generate_token( EXTERNAL_TOKEN_EMBEDDED, util::get_service_by_name($servicename), $USER->id, \context::instance_by_id($contextid) ); } /** * Delete all pre-built services (+ related tokens) and external functions information defined in the specified component. * * @param string $component name of component (moodle, etc.) */ function external_delete_descriptions($component) { util::delete_service_descriptions($component); } /** * Validate text field format against known FORMAT_XXX * * @param array $format the format to validate * @return the validated format * @throws coding_exception * @since Moodle 2.3 */ function external_validate_format($format) { return util::validate_format($format); } /** * Format the string to be returned properly as requested by the either the web service server, * either by an internally call. * The caller can change the format (raw) with the external_settings singleton * All web service servers must set this singleton when parsing the $_GET and $_POST. * *
 * Options are the same that in {@link format_string()} with some changes:
 *      filter      : Can be set to false to force filters off, else observes {@link external_settings}.
 * 
* * @param string $str The string to be filtered. Should be plain text, expect * possibly for multilang tags. * @param boolean $striplinks To strip any link in the result text. Moodle 1.8 default changed from false to true! MDL-8713 * @param context|int $contextorid The id of the context for the string or the context (affects filters). * @param array $options options array/object or courseid * @return string text * @since Moodle 3.0 */ function external_format_string($str, $context, $striplinks = true, $options = []) { if (!$context instanceof context) { $context = context::instance_by_id($context); } return util::format_string($str, $context, $striplinks, $options); } /** * Format the text to be returned properly as requested by the either the web service server, * either by an internally call. * The caller can change the format (raw, filter, file, fileurl) with the external_settings singleton * All web service servers must set this singleton when parsing the $_GET and $_POST. * *
 * Options are the same that in {@link format_text()} with some changes in defaults to provide backwards compatibility:
 *      trusted     :   If true the string won't be cleaned. Default false.
 *      noclean     :   If true the string won't be cleaned only if trusted is also true. Default false.
 *      nocache     :   If true the string will not be cached and will be formatted every call. Default false.
 *      filter      :   Can be set to false to force filters off, else observes {@link external_settings}.
 *      para        :   If true then the returned string will be wrapped in div tags. Default (different from format_text) false.
 *                      Default changed because div tags are not commonly needed.
 *      newlines    :   If true then lines newline breaks will be converted to HTML newline breaks. Default true.
 *      context     :   Not used! Using contextid parameter instead.
 *      overflowdiv :   If set to true the formatted text will be encased in a div with the class no-overflow before being
 *                      returned. Default false.
 *      allowid     :   If true then id attributes will not be removed, even when using htmlpurifier. Default (different from
 *                      format_text) true. Default changed id attributes are commonly needed.
 *      blanktarget :   If true all  tags will have target="_blank" added unless target is explicitly specified.
 * 
* * @param string $text The content that may contain ULRs in need of rewriting. * @param int $textformat The text format. * @param context|int $context This parameter and the next two identify the file area to use. * @param string $component * @param string $filearea helps identify the file area. * @param int $itemid helps identify the file area. * @param object/array $options text formatting options * @return array text + textformat * @since Moodle 2.3 * @since Moodle 3.2 component, filearea and itemid are optional parameters */ function external_format_text($text, $textformat, $context, $component = null, $filearea = null, $itemid = null, $options = null) { if (!$context instanceof context) { $context = context::instance_by_id($context); } return util::format_text($text, $textformat, $context, $component, $filearea, $itemid, $options); } /** * Generate or return an existing token for the current authenticated user. * This function is used for creating a valid token for users authenticathing via login/token.php or admin/tool/mobile/launch.php. * * @param stdClass $service external service object * @return stdClass token object * @since Moodle 3.2 */ function external_generate_token_for_current_user($service) { return util::generate_token_for_current_user($service); } /** * Set the last time a token was sent and trigger the \core\event\webservice_token_sent event. * * This function is used when a token is generated by the user via login/token.php or admin/tool/mobile/launch.php. * In order to protect the privatetoken, we remove it from the event params. * * @param stdClass $token token object * @since Moodle 3.2 */ function external_log_token_request($token): void { util::log_token_request($token); }