/** * Returns a list of forum posts for a discussion * * @param int $discussionid the post ids * @param string $sortby sort by this element (id, created or modified) * @param string $sortdirection sort direction: ASC or DESC * * @return array the forum post details * @since Moodle 2.7 */ public static function get_forum_discussion_posts($discussionid, $sortby = "created", $sortdirection = "DESC") { global $CFG, $DB, $USER, $PAGE; $posts = array(); $warnings = array(); // Validate the parameter. $params = self::validate_parameters(self::get_forum_discussion_posts_parameters(), array( 'discussionid' => $discussionid, 'sortby' => $sortby, 'sortdirection' => $sortdirection)); // Compact/extract functions are not recommended. $discussionid = $params['discussionid']; $sortby = $params['sortby']; $sortdirection = $params['sortdirection']; $sortallowedvalues = array('id', 'created', 'modified'); if (!in_array($sortby, $sortallowedvalues)) { throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' . 'allowed values are: ' . implode(',', $sortallowedvalues)); } $sortdirection = strtoupper($sortdirection); $directionallowedvalues = array('ASC', 'DESC'); if (!in_array($sortdirection, $directionallowedvalues)) { throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' . 'allowed values are: ' . implode(',', $directionallowedvalues)); } $discussion = $DB->get_record('forum_discussions', array('id' => $discussionid), '*', MUST_EXIST); $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST); $course = $DB->get_record('course', array('id' => $forum->course), '*', MUST_EXIST); $cm = get_coursemodule_from_instance('forum', $forum->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); // This require must be here, see mod/forum/discuss.php. require_once($CFG->dirroot . "/mod/forum/lib.php"); // Check they have the view forum capability. require_capability('mod/forum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'forum'); if (! $post = forum_get_post_full($discussion->firstpost)) { throw new moodle_exception('notexists', 'forum'); } // This function check groups, qanda, timed discussions, etc. if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm)) { throw new moodle_exception('noviewdiscussionspermission', 'forum'); } $canviewfullname = has_capability('moodle/site:viewfullnames', $modcontext); // We will add this field in the response. $canreply = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext); $forumtracked = forum_tp_is_tracked($forum); $sort = 'p.' . $sortby . ' ' . $sortdirection; $allposts = forum_get_all_discussion_posts($discussion->id, $sort, $forumtracked); foreach ($allposts as $post) { if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm)) { $warning = array(); $warning['item'] = 'post'; $warning['itemid'] = $post->id; $warning['warningcode'] = '1'; $warning['message'] = 'You can\'t see this post'; $warnings[] = $warning; continue; } // Function forum_get_all_discussion_posts adds postread field. // Note that the value returned can be a boolean or an integer. The WS expects a boolean. if (empty($post->postread)) { $post->postread = false; } else { $post->postread = true; } $post->canreply = $canreply; if (!empty($post->children)) { $post->children = array_keys($post->children); } else { $post->children = array(); } $userpicture = new user_picture($post); $userpicture->size = 1; // Size f1. $post->userpictureurl = $userpicture->get_url($PAGE)->out(false); $user = new stdclass(); $user->id = $post->userid; $user = username_load_fields_from_object($user, $post); $post->userfullname = fullname($user, $canviewfullname); // Rewrite embedded images URLs. list($post->message, $post->messageformat) = external_format_text($post->message, $post->messageformat, $modcontext->id, 'mod_forum', 'post', $post->id); // List attachments. if (!empty($post->attachment)) { $post->attachments = array(); $fs = get_file_storage(); if ($files = $fs->get_area_files($modcontext->id, 'mod_forum', 'attachment', $post->id, "filename", false)) { foreach ($files as $file) { $filename = $file->get_filename(); $fileurl = moodle_url::make_webservice_pluginfile_url( $modcontext->id, 'mod_forum', 'attachment', $post->id, '/', $filename); $post->attachments[] = array( 'filename' => $filename, 'mimetype' => $file->get_mimetype(), 'fileurl' => $fileurl->out(false) ); } } } $posts[] = $post; } $result = array(); $result['posts'] = $posts; $result['warnings'] = $warnings; return $result; }
/** * 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 * @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) { global $CFG; require_once $CFG->libdir . '/coursecatlib.php'; $warnings = array(); $parameters = array('criterianame' => $criterianame, 'criteriavalue' => $criteriavalue, 'page' => $page, 'perpage' => $perpage, 'requiredcapabilities' => $requiredcapabilities); $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']; $options = array(); if ($params['perpage'] != 0) { $offset = $params['page'] * $params['perpage']; $options = array('offset' => $offset, 'limit' => $params['perpage']); } // Search the courses. $courses = coursecat::search_courses($searchcriteria, $options, $params['requiredcapabilities']); $totalcount = coursecat::search_courses_count($searchcriteria, $options, $params['requiredcapabilities']); if (!empty($limittoenrolled)) { // Get the courses where the current user has access. $enrolled = enrol_get_my_courses(array('id', 'cacherev')); } $finalcourses = array(); $categoriescache = array(); foreach ($courses as $course) { if (!empty($limittoenrolled)) { // Filter out not enrolled courses. if (!isset($enrolled[$course->id])) { $totalcount--; continue; } } $coursecontext = context_course::instance($course->id); // Category information. if (!isset($categoriescache[$course->category])) { $categoriescache[$course->category] = coursecat::get($course->category); } $category = $categoriescache[$course->category]; // Retrieve course overfiew 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()); } // 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']); } // 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) = external_format_text($course->summary, $course->summaryformat, $coursecontext->id, 'course', 'summary', null); $displayname = get_course_display_name_for_list($course); $coursereturns = array(); $coursereturns['id'] = $course->id; $coursereturns['fullname'] = external_format_string($course->fullname, $coursecontext->id); $coursereturns['displayname'] = external_format_string($displayname, $coursecontext->id); $coursereturns['shortname'] = external_format_string($course->shortname, $coursecontext->id); $coursereturns['categoryid'] = $course->category; $coursereturns['categoryname'] = $category->name; $coursereturns['summary'] = $summary; $coursereturns['summaryformat'] = $summaryformat; $coursereturns['overviewfiles'] = $files; $coursereturns['contacts'] = $coursecontacts; $coursereturns['enrollmentmethods'] = $enroltypes; $finalcourses[] = $coursereturns; } return array('total' => $totalcount, 'courses' => $finalcourses, 'warnings' => $warnings); }
/** * Get the list of users in the given chat session. * * @param int $chatsid the chat session id * @return array of warnings and the user lists * @since Moodle 3.0 * @throws moodle_exception */ public static function get_chat_users($chatsid) { global $DB; $params = self::validate_parameters(self::get_chat_users_parameters(), array( 'chatsid' => $chatsid )); $warnings = array(); // Request and permission validation. if (!$chatuser = $DB->get_record('chat_users', array('sid' => $params['chatsid']))) { throw new moodle_exception('notlogged', 'chat'); } $chat = $DB->get_record('chat', array('id' => $chatuser->chatid), '*', MUST_EXIST); list($course, $cm) = get_course_and_cm_from_instance($chat, 'chat'); $context = context_module::instance($cm->id); self::validate_context($context); require_capability('mod/chat:chat', $context); // First, delete old users from the chats. chat_delete_old_users(); $users = chat_get_users($chatuser->chatid, $chatuser->groupid, $cm->groupingid); $returnedusers = array(); foreach ($users as $user) { $usercontext = context_user::instance($user->id, IGNORE_MISSING); $profileimageurl = ''; if ($usercontext) { $profileimageurl = moodle_url::make_webservice_pluginfile_url( $usercontext->id, 'user', 'icon', null, '/', 'f1')->out(false); } $returnedusers[] = array( 'id' => $user->id, 'fullname' => fullname($user), 'profileimageurl' => $profileimageurl ); } $result = array(); $result['users'] = $returnedusers; $result['warnings'] = $warnings; return $result; }
/** * Test get forum discussions paginated */ public function test_mod_forum_get_forum_discussions_paginated() { global $USER, $CFG, $DB; $this->resetAfterTest(true); // Set the CFG variable to allow track forums. $CFG->forum_trackreadposts = true; // Create a user who can track forums. $record = new stdClass(); $record->trackforums = true; $user1 = self::getDataGenerator()->create_user($record); // Create a bunch of other users to post. $user2 = self::getDataGenerator()->create_user(); $user3 = self::getDataGenerator()->create_user(); $user4 = self::getDataGenerator()->create_user(); // Set the first created user to the test user. self::setUser($user1); // Create courses to add the modules. $course1 = self::getDataGenerator()->create_course(); // First forum with tracking off. $record = new stdClass(); $record->course = $course1->id; $record->trackingtype = FORUM_TRACKING_OFF; $forum1 = self::getDataGenerator()->create_module('forum', $record); // Add discussions to the forums. $record = new stdClass(); $record->course = $course1->id; $record->userid = $user1->id; $record->forum = $forum1->id; $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); // Add three replies to the discussion 1 from different users. $record = new stdClass(); $record->discussion = $discussion1->id; $record->parent = $discussion1->firstpost; $record->userid = $user2->id; $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record); $record->parent = $discussion1reply1->id; $record->userid = $user3->id; $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record); $record->userid = $user4->id; $discussion1reply3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record); // Enrol the user in the first course. $enrol = enrol_get_plugin('manual'); // We don't use the dataGenerator as we need to get the $instance2 to unenrol later. $enrolinstances = enrol_get_instances($course1->id, true); foreach ($enrolinstances as $courseenrolinstance) { if ($courseenrolinstance->enrol == "manual") { $instance1 = $courseenrolinstance; break; } } $enrol->enrol_user($instance1, $user1->id); // Delete one user. delete_user($user4); // Assign capabilities to view discussions for forum 1. $cm = get_coursemodule_from_id('forum', $forum1->cmid, 0, false, MUST_EXIST); $context = context_module::instance($cm->id); $newrole = create_role('Role 2', 'role2', 'Role 2 description'); $this->assignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole); // Create what we expect to be returned when querying the forums. $post1 = $DB->get_record('forum_posts', array('id' => $discussion1->firstpost), '*', MUST_EXIST); $userpictureurl = moodle_url::make_webservice_pluginfile_url(context_user::instance($user1->id)->id, 'user', 'icon', null, '/', 'f1'); // We expect an empty URL since we deleted the user4. $usermodifiedpictureurl = ''; $expecteddiscussions = array('id' => $discussion1->firstpost, 'name' => $discussion1->name, 'groupid' => $discussion1->groupid, 'timemodified' => $discussion1reply3->created, 'usermodified' => $discussion1reply3->userid, 'timestart' => $discussion1->timestart, 'timeend' => $discussion1->timeend, 'discussion' => $discussion1->id, 'parent' => 0, 'userid' => $discussion1->userid, 'created' => $post1->created, 'modified' => $post1->modified, 'mailed' => $post1->mailed, 'subject' => $post1->subject, 'message' => $post1->message, 'messageformat' => $post1->messageformat, 'messagetrust' => $post1->messagetrust, 'attachment' => $post1->attachment, 'totalscore' => $post1->totalscore, 'mailnow' => $post1->mailnow, 'userfullname' => fullname($user1), 'usermodifiedfullname' => fullname($user4), 'userpictureurl' => $userpictureurl, 'usermodifiedpictureurl' => $usermodifiedpictureurl, 'numreplies' => 3, 'numunread' => 0); // Call the external function passing forum id. $discussions = mod_forum_external::get_forum_discussions_paginated($forum1->id); $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions); $expectedreturn = array('discussions' => array($expecteddiscussions), 'warnings' => array()); $this->assertEquals($expectedreturn, $discussions); // Call without required view discussion capability. $this->unassignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole); try { mod_forum_external::get_forum_discussions_paginated($forum1->id); $this->fail('Exception expected due to missing capability.'); } catch (moodle_exception $e) { $this->assertEquals('noviewdiscussionspermission', $e->errorcode); } // Unenrol user from second course. $enrol->unenrol_user($instance1, $user1->id); // Call for the second course we unenrolled the user from, make sure exception thrown. try { mod_forum_external::get_forum_discussions_paginated($forum1->id); $this->fail('Exception expected due to being unenrolled from the course.'); } catch (moodle_exception $e) { $this->assertEquals('requireloginerror', $e->errorcode); } }
/** * Returns an array of courses the user is enrolled in, and for each course all of the seplments that the user can * view within that course. * * @param array $courseids An optional array of course ids. If provided only seplments within the given course * will be returned. If the user is not enrolled in 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. * @return An array of courses and warnings. * @since Moodle 2.4 */ public static function get_seplments($courseids = array(), $capabilities = array()) { global $USER, $DB, $CFG; require_once "{$CFG->dirroot}/mod/sepl/locallib.php"; $params = self::validate_parameters(self::get_seplments_parameters(), array('courseids' => $courseids, 'capabilities' => $capabilities)); $warnings = array(); $fields = 'sortorder,shortname,fullname,timemodified'; $courses = enrol_get_users_courses($USER->id, true, $fields); // Used to test for ids that have been requested but can't be returned. if (count($params['courseids']) > 0) { foreach ($params['courseids'] as $courseid) { if (!in_array($courseid, array_keys($courses))) { unset($courses[$courseid]); $warnings[] = array('item' => 'course', 'itemid' => $courseid, 'warningcode' => '2', 'message' => 'User is not enrolled or does not have requested capability'); } } } foreach ($courses as $id => $course) { if (count($params['courseids']) > 0 && !in_array($id, $params['courseids'])) { unset($courses[$id]); } $context = context_course::instance($id); try { self::validate_context($context); } catch (Exception $e) { unset($courses[$id]); $warnings[] = array('item' => 'course', 'itemid' => $id, 'warningcode' => '1', 'message' => 'No access rights in course context ' . $e->getMessage() . $e->getTraceAsString()); continue; } if (count($params['capabilities']) > 0 && !has_all_capabilities($params['capabilities'], $context)) { unset($courses[$id]); } } $extrafields = 'm.id as seplmentid, ' . 'm.course, ' . 'm.nosubmissions, ' . 'm.submissiondrafts, ' . 'm.sendnotifications, ' . 'm.sendlatenotifications, ' . 'm.sendstudentnotifications, ' . 'm.duedate, ' . 'm.allowsubmissionsfromdate, ' . 'm.grade, ' . 'm.timemodified, ' . 'm.completionsubmit, ' . 'm.cutoffdate, ' . 'm.teamsubmission, ' . 'm.requireallteammemberssubmit, ' . 'm.teamsubmissiongroupingid, ' . 'm.blindmarking, ' . 'm.revealidentities, ' . 'm.attemptreopenmethod, ' . 'm.maxattempts, ' . 'm.markingworkflow, ' . 'm.markingallocation, ' . 'm.requiresubmissionstatement, ' . 'm.intro, ' . 'm.introformat'; $coursearray = array(); foreach ($courses as $id => $course) { $seplmentarray = array(); // Get a list of seplments for the course. if ($modules = get_coursemodules_in_course('sepl', $courses[$id]->id, $extrafields)) { foreach ($modules as $module) { $context = context_module::instance($module->id); try { self::validate_context($context); require_capability('mod/sepl:view', $context); } catch (Exception $e) { $warnings[] = array('item' => 'module', 'itemid' => $module->id, 'warningcode' => '1', 'message' => 'No access rights in module context'); continue; } $configrecords = $DB->get_recordset('sepl_plugin_config', array('seplment' => $module->seplmentid)); $configarray = array(); foreach ($configrecords as $configrecord) { $configarray[] = array('id' => $configrecord->id, 'seplment' => $configrecord->seplment, 'plugin' => $configrecord->plugin, 'subtype' => $configrecord->subtype, 'name' => $configrecord->name, 'value' => $configrecord->value); } $configrecords->close(); $seplment = array('id' => $module->seplmentid, 'cmid' => $module->id, 'course' => $module->course, 'name' => $module->name, 'nosubmissions' => $module->nosubmissions, 'submissiondrafts' => $module->submissiondrafts, 'sendnotifications' => $module->sendnotifications, 'sendlatenotifications' => $module->sendlatenotifications, 'sendstudentnotifications' => $module->sendstudentnotifications, 'duedate' => $module->duedate, 'allowsubmissionsfromdate' => $module->allowsubmissionsfromdate, 'grade' => $module->grade, 'timemodified' => $module->timemodified, 'completionsubmit' => $module->completionsubmit, 'cutoffdate' => $module->cutoffdate, 'teamsubmission' => $module->teamsubmission, 'requireallteammemberssubmit' => $module->requireallteammemberssubmit, 'teamsubmissiongroupingid' => $module->teamsubmissiongroupingid, 'blindmarking' => $module->blindmarking, 'revealidentities' => $module->revealidentities, 'attemptreopenmethod' => $module->attemptreopenmethod, 'maxattempts' => $module->maxattempts, 'markingworkflow' => $module->markingworkflow, 'markingallocation' => $module->markingallocation, 'requiresubmissionstatement' => $module->requiresubmissionstatement, 'configs' => $configarray); // Return or not intro and file attachments depending on the plugin settings. $sepl = new sepl($context, null, null); if ($sepl->show_intro()) { list($seplment['intro'], $seplment['introformat']) = external_format_text($module->intro, $module->introformat, $context->id, 'mod_sepl', ASSIGN_INTROATTACHMENT_FILEAREA, 0); $fs = get_file_storage(); if ($files = $fs->get_area_files($context->id, 'mod_sepl', ASSIGN_INTROATTACHMENT_FILEAREA, 0, 'timemodified', false)) { $seplment['introattachments'] = array(); foreach ($files as $file) { $filename = $file->get_filename(); $seplment['introattachments'][] = array('filename' => $filename, 'mimetype' => $file->get_mimetype(), 'fileurl' => moodle_url::make_webservice_pluginfile_url($context->id, 'mod_sepl', ASSIGN_INTROATTACHMENT_FILEAREA, 0, '/', $filename)->out(false)); } } } $seplmentarray[] = $seplment; } } $coursearray[] = array('id' => $courses[$id]->id, 'fullname' => $courses[$id]->fullname, 'shortname' => $courses[$id]->shortname, 'timemodified' => $courses[$id]->timemodified, 'seplments' => $seplmentarray); } $result = array('courses' => $coursearray, 'warnings' => $warnings); return $result; }
/** * Test get_subwiki_files using an individual wiki with visible groups. */ public function test_get_subwiki_files_visible_groups_individual() { // Create testing data. $this->create_individual_wikis_with_groups(); $this->setUser($this->student); // Add a file as subwiki attachment in the student group 1 subwiki. $fs = get_file_storage(); $contextwiki = context_module::instance($this->wikivisind->cmid); $file = array('component' => 'mod_wiki', 'filearea' => 'attachments', 'contextid' => $contextwiki->id, 'itemid' => $this->fpvisg1indstu->subwikiid, 'filename' => 'image.jpg', 'filepath' => '/', 'timemodified' => time()); $content = 'IMAGE'; $fs->create_file_from_string($file, $content); $expectedfile = array('filename' => $file['filename'], 'filepath' => $file['filepath'], 'mimetype' => 'image/jpeg', 'filesize' => strlen($content), 'timemodified' => $file['timemodified'], 'fileurl' => moodle_url::make_webservice_pluginfile_url($file['contextid'], $file['component'], $file['filearea'], $file['itemid'], $file['filepath'], $file['filename'])); // Call the WS and check that it returns this file. $result = mod_wiki_external::get_subwiki_files($this->wikivisind->id, $this->group1->id); $result = external_api::clean_returnvalue(mod_wiki_external::get_subwiki_files_returns(), $result); $this->assertCount(1, $result['files']); $this->assertEquals($expectedfile, $result['files'][0]); // Now check that a teacher can see it too. $this->setUser($this->teacher); $result = mod_wiki_external::get_subwiki_files($this->wikivisind->id, $this->group1->id, $this->student->id); $result = external_api::clean_returnvalue(mod_wiki_external::get_subwiki_files_returns(), $result); $this->assertCount(1, $result['files']); $this->assertEquals($expectedfile, $result['files'][0]); }
/** * 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.timemodified, ' . 'm.completionsubmit, ' . 'm.cutoffdate, ' . 'm.teamsubmission, ' . 'm.requireallteammemberssubmit, ' . 'm.teamsubmissiongroupingid, ' . 'm.blindmarking, ' . 'm.revealidentities, ' . 'm.attemptreopenmethod, ' . 'm.maxattempts, ' . 'm.markingworkflow, ' . 'm.markingallocation, ' . 'm.requiresubmissionstatement, ' . 'm.preventsubmissionnotingroup, ' . 'm.intro, ' . 'm.introformat'; $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; } $configrecords = $DB->get_recordset('assign_plugin_config', array('assignment' => $module->assignmentid)); $configarray = array(); foreach ($configrecords as $configrecord) { $configarray[] = array('id' => $configrecord->id, 'assignment' => $configrecord->assignment, 'plugin' => $configrecord->plugin, 'subtype' => $configrecord->subtype, 'name' => $configrecord->name, 'value' => $configrecord->value); } $configrecords->close(); $assignment = array('id' => $module->assignmentid, 'cmid' => $module->id, 'course' => $module->course, 'name' => $module->name, 'nosubmissions' => $module->nosubmissions, 'submissiondrafts' => $module->submissiondrafts, 'sendnotifications' => $module->sendnotifications, 'sendlatenotifications' => $module->sendlatenotifications, 'sendstudentnotifications' => $module->sendstudentnotifications, 'duedate' => $module->duedate, 'allowsubmissionsfromdate' => $module->allowsubmissionsfromdate, 'grade' => $module->grade, 'timemodified' => $module->timemodified, 'completionsubmit' => $module->completionsubmit, 'cutoffdate' => $module->cutoffdate, 'teamsubmission' => $module->teamsubmission, 'requireallteammemberssubmit' => $module->requireallteammemberssubmit, 'teamsubmissiongroupingid' => $module->teamsubmissiongroupingid, 'blindmarking' => $module->blindmarking, 'revealidentities' => $module->revealidentities, 'attemptreopenmethod' => $module->attemptreopenmethod, 'maxattempts' => $module->maxattempts, 'markingworkflow' => $module->markingworkflow, 'markingallocation' => $module->markingallocation, 'requiresubmissionstatement' => $module->requiresubmissionstatement, 'preventsubmissionnotingroup' => $module->preventsubmissionnotingroup, 'configs' => $configarray); // Return or not intro and file attachments depending on the plugin settings. $assign = new assign($context, null, null); if ($assign->show_intro()) { list($assignment['intro'], $assignment['introformat']) = external_format_text($module->intro, $module->introformat, $context->id, 'mod_assign', 'intro', null); $assignment['introfiles'] = external_util::get_area_files($context->id, 'mod_assign', 'intro', false, false); $fs = get_file_storage(); if ($files = $fs->get_area_files($context->id, 'mod_assign', ASSIGN_INTROATTACHMENT_FILEAREA, 0, 'timemodified', false)) { $assignment['introattachments'] = array(); foreach ($files as $file) { $filename = $file->get_filename(); $assignment['introattachments'][] = array('filename' => $filename, 'mimetype' => $file->get_mimetype(), 'fileurl' => moodle_url::make_webservice_pluginfile_url($context->id, 'mod_assign', ASSIGN_INTROATTACHMENT_FILEAREA, 0, '/', $filename)->out(false)); } } } $assignmentarray[] = $assignment; } } $coursearray[] = array('id' => $courses[$id]->id, 'fullname' => external_format_string($courses[$id]->fullname, $course->contextid), 'shortname' => external_format_string($courses[$id]->shortname, $course->contextid), 'timemodified' => $courses[$id]->timemodified, 'assignments' => $assignmentarray); } $result = array('courses' => $coursearray, 'warnings' => $warnings); return $result; }
/** * Export book resource contents * * @param stdClass $cm Course module object * @param string $baseurl Base URL for file downloads * @return array of file content */ function book_export_contents($cm, $baseurl) { global $DB; $contents = array(); $context = context_module::instance($cm->id); $book = $DB->get_record('book', array('id' => $cm->instance), '*', MUST_EXIST); $fs = get_file_storage(); $chapters = $DB->get_records('book_chapters', array('bookid' => $book->id), 'pagenum'); $structure = array(); $currentchapter = 0; foreach ($chapters as $chapter) { if ($chapter->hidden) { continue; } // Generate the book structure. $thischapter = array("title" => format_string($chapter->title, true, array('context' => $context)), "href" => $chapter->id . "/index.html", "level" => 0, "subitems" => array()); // Main chapter. if (!$chapter->subchapter) { $currentchapter = $chapter->pagenum; $structure[$currentchapter] = $thischapter; } else { // Subchapter. $thischapter['level'] = 1; $structure[$currentchapter]["subitems"][] = $thischapter; } // Export the chapter contents. // Main content (html). $filename = 'index.html'; $chapterindexfile = array(); $chapterindexfile['type'] = 'file'; $chapterindexfile['filename'] = $filename; // Each chapter in a subdirectory. $chapterindexfile['filepath'] = "/{$chapter->id}/"; $chapterindexfile['filesize'] = 0; $chapterindexfile['fileurl'] = moodle_url::make_webservice_pluginfile_url($context->id, 'mod_book', 'chapter', $chapter->id, '/', 'index.html')->out(false); $chapterindexfile['timecreated'] = $book->timecreated; $chapterindexfile['timemodified'] = $book->timemodified; $chapterindexfile['content'] = format_string($chapter->title, true, array('context' => $context)); $chapterindexfile['sortorder'] = 0; $chapterindexfile['userid'] = null; $chapterindexfile['author'] = null; $chapterindexfile['license'] = null; $contents[] = $chapterindexfile; // Chapter files (images usually). $files = $fs->get_area_files($context->id, 'mod_book', 'chapter', $chapter->id, 'sortorder DESC, id ASC', false); foreach ($files as $fileinfo) { $file = array(); $file['type'] = 'file'; $file['filename'] = $fileinfo->get_filename(); $file['filepath'] = "/{$chapter->id}" . $fileinfo->get_filepath(); $file['filesize'] = $fileinfo->get_filesize(); $file['fileurl'] = moodle_url::make_webservice_pluginfile_url($context->id, 'mod_book', 'chapter', $chapter->id, $fileinfo->get_filepath(), $fileinfo->get_filename())->out(false); $file['timecreated'] = $fileinfo->get_timecreated(); $file['timemodified'] = $fileinfo->get_timemodified(); $file['sortorder'] = $fileinfo->get_sortorder(); $file['userid'] = $fileinfo->get_userid(); $file['author'] = $fileinfo->get_author(); $file['license'] = $fileinfo->get_license(); $contents[] = $file; } } // First content is the structure in encoded JSON format. $structurefile = array(); $structurefile['type'] = 'content'; $structurefile['filename'] = 'structure'; $structurefile['filepath'] = "/"; $structurefile['filesize'] = 0; $structurefile['fileurl'] = null; $structurefile['timecreated'] = $book->timecreated; $structurefile['timemodified'] = $book->timemodified; $structurefile['content'] = json_encode(array_values($structure)); $structurefile['sortorder'] = 0; $structurefile['userid'] = null; $structurefile['author'] = null; $structurefile['license'] = null; // Add it as first element. array_unshift($contents, $structurefile); return $contents; }
/** * Returns the list of badges awarded to a user. * * @param int $userid user id * @param int $courseid course id * @param int $page page of records to return * @param int $perpage number of records to return per page * @param string $search a simple string to search for * @param bool $onlypublic whether to return only public badges * @return array array containing warnings and the awarded badges * @since Moodle 3.1 * @throws moodle_exception */ public static function get_user_badges($userid = 0, $courseid = 0, $page = 0, $perpage = 0, $search = '', $onlypublic = false) { global $CFG, $USER; $warnings = array(); $params = array('userid' => $userid, 'courseid' => $courseid, 'page' => $page, 'perpage' => $perpage, 'search' => $search, 'onlypublic' => $onlypublic); $params = self::validate_parameters(self::get_user_badges_parameters(), $params); if (empty($CFG->enablebadges)) { throw new moodle_exception('badgesdisabled', 'badges'); } if (empty($CFG->badges_allowcoursebadges) && $params['courseid'] != 0) { throw new moodle_exception('coursebadgesdisabled', 'badges'); } // Default value for userid. if (empty($params['userid'])) { $params['userid'] = $USER->id; } // Validate the user. $user = core_user::get_user($params['userid'], '*', MUST_EXIST); core_user::require_active_user($user); $usercontext = context_user::instance($user->id); self::validate_context($usercontext); if ($USER->id != $user->id) { require_capability('moodle/badges:viewotherbadges', $usercontext); // We are looking other user's badges, we must retrieve only public badges. $params['onlypublic'] = true; } $userbadges = badges_get_user_badges($user->id, $params['courseid'], $params['page'], $params['perpage'], $params['search'], $params['onlypublic']); $result = array(); $result['badges'] = array(); $result['warnings'] = $warnings; foreach ($userbadges as $badge) { $context = $badge->type == BADGE_TYPE_SITE ? context_system::instance() : context_course::instance($badge->courseid); $badge->badgeurl = moodle_url::make_webservice_pluginfile_url($context->id, 'badges', 'badgeimage', $badge->id, '/', 'f1')->out(false); // Return all the information if we are requesting our own badges. // Or, if we have permissions for configuring badges in the badge context. if ($USER->id == $user->id or has_capability('moodle/badges:configuredetails', $context)) { $result['badges'][] = (array) $badge; } else { $result['badges'][] = array('name' => $badge->name, 'description' => $badge->description, 'badgeurl' => $badge->badgeurl, 'issuername' => $badge->issuername, 'issuerurl' => $badge->issuerurl, 'issuercontact' => $badge->issuercontact, 'uniquehash' => $badge->uniquehash, 'dateissued' => $badge->dateissued, 'dateexpire' => $badge->dateexpire); } } return $result; }
/** * Fill in an entry object. * * This adds additional required fields for the external function to return. * * @param stdClass $entry The entry. * @param context $context The context the entry belongs to. * @return void */ protected static function fill_entry_details($entry, $context) { global $PAGE; $canviewfullnames = has_capability('moodle/site:viewfullnames', $context); // Format concept and definition. $entry->concept = external_format_string($entry->concept, $context->id); list($entry->definition, $entry->definitionformat) = external_format_text($entry->definition, $entry->definitionformat, $context->id, 'mod_glossary', 'entry', $entry->id); // Author details. $user = mod_glossary_entry_query_builder::get_user_from_record($entry); $userpicture = new user_picture($user); $userpicture->size = 1; $entry->userfullname = fullname($user, $canviewfullnames); $entry->userpictureurl = $userpicture->get_url($PAGE)->out(false); // Fetch attachments. $entry->attachment = !empty($entry->attachment) ? 1 : 0; $entry->attachments = array(); if ($entry->attachment) { $fs = get_file_storage(); if ($files = $fs->get_area_files($context->id, 'mod_glossary', 'attachment', $entry->id, 'filename', false)) { foreach ($files as $file) { $filename = $file->get_filename(); $fileurl = moodle_url::make_webservice_pluginfile_url($context->id, 'mod_glossary', 'attachment', $entry->id, '/', $filename); $entry->attachments[] = array('filename' => $filename, 'mimetype' => $file->get_mimetype(), 'fileurl' => $fileurl->out(false)); } } } }
/** * Retrieve a list of users blocked * * @param int $userid the user whose blocked users we want to retrieve * @return external_description * @since 2.9 */ public static function get_blocked_users($userid) { global $CFG, $USER; require_once $CFG->dirroot . "/message/lib.php"; // Warnings array, it can be empty at the end but is mandatory. $warnings = array(); // Validate params. $params = array('userid' => $userid); $params = self::validate_parameters(self::get_blocked_users_parameters(), $params); $userid = $params['userid']; // Validate context. $context = context_system::instance(); self::validate_context($context); // Check if private messaging between users is allowed. if (empty($CFG->messaging)) { throw new moodle_exception('disabled', 'message'); } $user = core_user::get_user($userid, 'id', MUST_EXIST); // Check if we have permissions for retrieve the information. if ($userid != $USER->id and !has_capability('moodle/site:readallmessages', $context)) { throw new moodle_exception('accessdenied', 'admin'); } // Now, we can get safely all the blocked users. $users = message_get_blocked_users($user); $blockedusers = array(); foreach ($users as $user) { $newuser = array('id' => $user->id, 'fullname' => fullname($user)); $newuser['profileimageurl'] = moodle_url::make_webservice_pluginfile_url(context_user::instance($user->id)->id, 'user', 'icon', null, '/', 'f1')->out(false); $blockedusers[] = $newuser; } $results = array('users' => $blockedusers, 'warnings' => $warnings); return $results; }
/** * Test get forum posts */ public function test_mod_forum_get_forum_discussion_posts() { global $CFG, $PAGE; $this->resetAfterTest(true); // Set the CFG variable to allow track forums. $CFG->forum_trackreadposts = true; // Create a user who can track forums. $record = new stdClass(); $record->trackforums = true; $user1 = self::getDataGenerator()->create_user($record); // Create a bunch of other users to post. $user2 = self::getDataGenerator()->create_user(); $user3 = self::getDataGenerator()->create_user(); // Set the first created user to the test user. self::setUser($user1); // Create course to add the module. $course1 = self::getDataGenerator()->create_course(); // Forum with tracking off. $record = new stdClass(); $record->course = $course1->id; $record->trackingtype = FORUM_TRACKING_OFF; $forum1 = self::getDataGenerator()->create_module('forum', $record); $forum1context = context_module::instance($forum1->cmid); // Add discussions to the forums. $record = new stdClass(); $record->course = $course1->id; $record->userid = $user1->id; $record->forum = $forum1->id; $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); $record = new stdClass(); $record->course = $course1->id; $record->userid = $user2->id; $record->forum = $forum1->id; $discussion2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); // Add 2 replies to the discussion 1 from different users. $record = new stdClass(); $record->discussion = $discussion1->id; $record->parent = $discussion1->firstpost; $record->userid = $user2->id; $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record); $filename = 'shouldbeanimage.jpg'; // Add a fake inline image to the post. $filerecordinline = array('contextid' => $forum1context->id, 'component' => 'mod_forum', 'filearea' => 'post', 'itemid' => $discussion1reply1->id, 'filepath' => '/', 'filename' => $filename); $fs = get_file_storage(); $timepost = time(); $fs->create_file_from_string($filerecordinline, 'image contents (not really)'); $record->parent = $discussion1reply1->id; $record->userid = $user3->id; $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record); // Enrol the user in the course. $enrol = enrol_get_plugin('manual'); // Following line enrol and assign default role id to the user. // So the user automatically gets mod/forum:viewdiscussion on all forums of the course. $this->getDataGenerator()->enrol_user($user1->id, $course1->id); $this->getDataGenerator()->enrol_user($user2->id, $course1->id); // Delete one user, to test that we still receive posts by this user. delete_user($user3); // Create what we expect to be returned when querying the discussion. $expectedposts = array('posts' => array(), 'warnings' => array()); // User pictures are initially empty, we should get the links once the external function is called. $expectedposts['posts'][] = array('id' => $discussion1reply2->id, 'discussion' => $discussion1reply2->discussion, 'parent' => $discussion1reply2->parent, 'userid' => (int) $discussion1reply2->userid, 'created' => $discussion1reply2->created, 'modified' => $discussion1reply2->modified, 'mailed' => $discussion1reply2->mailed, 'subject' => $discussion1reply2->subject, 'message' => file_rewrite_pluginfile_urls($discussion1reply2->message, 'pluginfile.php', $forum1context->id, 'mod_forum', 'post', $discussion1reply2->id), 'messageformat' => 1, 'messagetrust' => $discussion1reply2->messagetrust, 'attachment' => $discussion1reply2->attachment, 'totalscore' => $discussion1reply2->totalscore, 'mailnow' => $discussion1reply2->mailnow, 'children' => array(), 'canreply' => true, 'postread' => false, 'userfullname' => fullname($user3), 'userpictureurl' => ''); $expectedposts['posts'][] = array('id' => $discussion1reply1->id, 'discussion' => $discussion1reply1->discussion, 'parent' => $discussion1reply1->parent, 'userid' => (int) $discussion1reply1->userid, 'created' => $discussion1reply1->created, 'modified' => $discussion1reply1->modified, 'mailed' => $discussion1reply1->mailed, 'subject' => $discussion1reply1->subject, 'message' => file_rewrite_pluginfile_urls($discussion1reply1->message, 'pluginfile.php', $forum1context->id, 'mod_forum', 'post', $discussion1reply1->id), 'messageformat' => 1, 'messagetrust' => $discussion1reply1->messagetrust, 'attachment' => $discussion1reply1->attachment, 'messageinlinefiles' => array(array('filename' => $filename, 'filepath' => '/', 'filesize' => '27', 'fileurl' => moodle_url::make_webservice_pluginfile_url($forum1context->id, 'mod_forum', 'post', $discussion1reply1->id, '/', $filename), 'timemodified' => $timepost, 'mimetype' => 'image/jpeg')), 'totalscore' => $discussion1reply1->totalscore, 'mailnow' => $discussion1reply1->mailnow, 'children' => array($discussion1reply2->id), 'canreply' => true, 'postread' => false, 'userfullname' => fullname($user2), 'userpictureurl' => ''); // Test a discussion with two additional posts (total 3 posts). $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC'); $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts); $this->assertEquals(3, count($posts['posts'])); // Generate here the pictures because we need to wait to the external function to init the theme. $userpicture = new user_picture($user3); $userpicture->size = 1; // Size f1. $expectedposts['posts'][0]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false); $userpicture = new user_picture($user2); $userpicture->size = 1; // Size f1. $expectedposts['posts'][1]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false); // Unset the initial discussion post. array_pop($posts['posts']); $this->assertEquals($expectedposts, $posts); // Test discussion without additional posts. There should be only one post (the one created by the discussion). $posts = mod_forum_external::get_forum_discussion_posts($discussion2->id, 'modified', 'DESC'); $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts); $this->assertEquals(1, count($posts['posts'])); }
/** * Retrieve a list of ratings for a given item (forum post etc) * * @param string $contextlevel course, module, user... * @param int $instanceid the instance if for the context element * @param string $component the name of the component * @param string $ratingarea rating area * @param int $itemid the item id * @param int $scaleid the scale id * @param string $sort sql order (firstname, rating or timemodified) * @return array Result and possible warnings * @throws moodle_exception * @since Moodle 2.9 */ public static function get_item_ratings($contextlevel, $instanceid, $component, $ratingarea, $itemid, $scaleid, $sort) { global $USER; $warnings = array(); $arrayparams = array('contextlevel' => $contextlevel, 'instanceid' => $instanceid, 'component' => $component, 'ratingarea' => $ratingarea, 'itemid' => $itemid, 'scaleid' => $scaleid, 'sort' => $sort); // Validate and normalize parameters. $params = self::validate_parameters(self::get_item_ratings_parameters(), $arrayparams); $context = self::get_context_from_params($params); self::validate_context($context); // Minimal capability required. $callbackparams = array('contextid' => $context->id, 'component' => $component, 'ratingarea' => $ratingarea, 'itemid' => $itemid, 'scaleid' => $scaleid); if (!has_capability('moodle/rating:view', $context) || !component_callback($component, 'rating_can_see_item_ratings', array($callbackparams), true)) { throw new moodle_exception('noviewrate', 'rating'); } list($context, $course, $cm) = get_context_info_array($context->id); // Can we see all ratings? $canviewallratings = has_capability('moodle/rating:viewall', $context); // Create the Sql sort order string. switch ($params['sort']) { case 'firstname': $sqlsort = "u.firstname ASC"; break; case 'rating': $sqlsort = "r.rating ASC"; break; default: $sqlsort = "r.timemodified ASC"; } $ratingoptions = new stdClass(); $ratingoptions->context = $context; $ratingoptions->component = $params['component']; $ratingoptions->ratingarea = $params['ratingarea']; $ratingoptions->itemid = $params['itemid']; $ratingoptions->sort = $sqlsort; $rm = new rating_manager(); $ratings = $rm->get_all_ratings_for_item($ratingoptions); $scalemenu = make_grades_menu($params['scaleid']); // If the scale was changed after ratings were submitted some ratings may have a value above the current maximum. // We can't just do count($scalemenu) - 1 as custom scales start at index 1, not 0. $maxrating = max(array_keys($scalemenu)); $results = array(); foreach ($ratings as $rating) { if ($canviewallratings || $USER->id == $rating->userid) { if ($rating->rating > $maxrating) { $rating->rating = $maxrating; } $profileimageurl = ''; // We can have ratings from deleted users. In this case, those users don't have a valid context. $usercontext = context_user::instance($rating->userid, IGNORE_MISSING); if ($usercontext) { $profileimageurl = moodle_url::make_webservice_pluginfile_url($usercontext->id, 'user', 'icon', null, '/', 'f1')->out(false); } $result = array(); $result['id'] = $rating->id; $result['userid'] = $rating->userid; $result['userpictureurl'] = $profileimageurl; $result['userfullname'] = fullname($rating); $result['rating'] = $scalemenu[$rating->rating]; $result['timemodified'] = $rating->timemodified; $results[] = $result; } } return array('ratings' => $results, 'warnings' => $warnings); }
/** * Returns a list of scorms in a provided list of courses, * if no list is provided all scorms that the user can view will be returned. * * @param array $courseids the course ids * @return array the scorm details * @since Moodle 3.0 */ public static function get_scorms_by_courses($courseids = array()) { global $CFG; $returnedscorms = array(); $warnings = array(); $params = self::validate_parameters(self::get_scorms_by_courses_parameters(), array('courseids' => $courseids)); $courses = array(); if (empty($params['courseids'])) { $courses = enrol_get_my_courses(); $params['courseids'] = array_keys($courses); } // Ensure there are courseids to loop through. if (!empty($params['courseids'])) { list($courses, $warnings) = external_util::validate_courses($params['courseids'], $courses); // Get the scorms in this course, this function checks users visibility permissions. // We can avoid then additional validate_context calls. $scorms = get_all_instances_in_courses("scorm", $courses); $fs = get_file_storage(); foreach ($scorms as $scorm) { $context = context_module::instance($scorm->coursemodule); // Entry to return. $module = array(); // First, we return information that any user can see in (or can deduce from) the web interface. $module['id'] = $scorm->id; $module['coursemodule'] = $scorm->coursemodule; $module['course'] = $scorm->course; $module['name'] = external_format_string($scorm->name, $context->id); list($module['intro'], $module['introformat']) = external_format_text($scorm->intro, $scorm->introformat, $context->id, 'mod_scorm', 'intro', $scorm->id); // Check if the SCORM open and return warnings if so. list($open, $openwarnings) = scorm_get_availability_status($scorm, true, $context); if (!$open) { foreach ($openwarnings as $warningkey => $warningdata) { $warnings[] = array('item' => 'scorm', 'itemid' => $scorm->id, 'warningcode' => $warningkey, 'message' => get_string($warningkey, 'scorm', $warningdata)); } } else { $module['packagesize'] = 0; // SCORM size. if ($scorm->scormtype === SCORM_TYPE_LOCAL or $scorm->scormtype === SCORM_TYPE_LOCALSYNC) { if ($packagefile = $fs->get_file($context->id, 'mod_scorm', 'package', 0, '/', $scorm->reference)) { $module['packagesize'] = $packagefile->get_filesize(); // Download URL. $module['packageurl'] = moodle_url::make_webservice_pluginfile_url($context->id, 'mod_scorm', 'package', 0, '/', $scorm->reference)->out(false); } } $module['protectpackagedownloads'] = get_config('scorm', 'protectpackagedownloads'); $viewablefields = array('version', 'maxgrade', 'grademethod', 'whatgrade', 'maxattempt', 'forcecompleted', 'forcenewattempt', 'lastattemptlock', 'displayattemptstatus', 'displaycoursestructure', 'sha1hash', 'md5hash', 'revision', 'launch', 'skipview', 'hidebrowse', 'hidetoc', 'nav', 'navpositionleft', 'navpositiontop', 'auto', 'popup', 'width', 'height', 'timeopen', 'timeclose', 'displayactivityname', 'scormtype', 'reference'); // Check additional permissions for returning optional private settings. if (has_capability('moodle/course:manageactivities', $context)) { $additionalfields = array('updatefreq', 'options', 'completionstatusrequired', 'completionscorerequired', 'autocommit', 'timemodified', 'section', 'visible', 'groupmode', 'groupingid'); $viewablefields = array_merge($viewablefields, $additionalfields); } foreach ($viewablefields as $field) { $module[$field] = $scorm->{$field}; } } $returnedscorms[] = $module; } } $result = array(); $result['scorms'] = $returnedscorms; $result['warnings'] = $warnings; return $result; }
/** * Test get forum posts */ public function test_mod_forum_get_forum_discussion_posts() { global $CFG; $this->resetAfterTest(true); // Set the CFG variable to allow track forums. $CFG->forum_trackreadposts = true; // Create a user who can track forums. $record = new stdClass(); $record->trackforums = true; $user1 = self::getDataGenerator()->create_user($record); // Create a bunch of other users to post. $user2 = self::getDataGenerator()->create_user(); $user3 = self::getDataGenerator()->create_user(); // Set the first created user to the test user. self::setUser($user1); // Create course to add the module. $course1 = self::getDataGenerator()->create_course(); // Forum with tracking off. $record = new stdClass(); $record->course = $course1->id; $record->trackingtype = FORUM_TRACKING_OFF; $forum1 = self::getDataGenerator()->create_module('forum', $record); $forum1context = context_module::instance($forum1->cmid); // Add discussions to the forums. $record = new stdClass(); $record->course = $course1->id; $record->userid = $user1->id; $record->forum = $forum1->id; $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); $record = new stdClass(); $record->course = $course1->id; $record->userid = $user2->id; $record->forum = $forum1->id; $discussion2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); // Add 2 replies to the discussion 1 from different users. $record = new stdClass(); $record->discussion = $discussion1->id; $record->parent = $discussion1->firstpost; $record->userid = $user2->id; $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record); $record->parent = $discussion1reply1->id; $record->userid = $user3->id; $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record); // Enrol the user in the course. $enrol = enrol_get_plugin('manual'); // Following line enrol and assign default role id to the user. // So the user automatically gets mod/forum:viewdiscussion on all forums of the course. $this->getDataGenerator()->enrol_user($user1->id, $course1->id); $this->getDataGenerator()->enrol_user($user2->id, $course1->id); $this->getDataGenerator()->enrol_user($user3->id, $course1->id); // Create what we expect to be returned when querying the discussion. $expectedposts = array('posts' => array(), 'warnings' => array()); $userpictureurl = moodle_url::make_webservice_pluginfile_url(context_user::instance($discussion1reply2->userid)->id, 'user', 'icon', null, '/', 'f1')->out(false); $expectedposts['posts'][] = array('id' => $discussion1reply2->id, 'discussion' => $discussion1reply2->discussion, 'parent' => $discussion1reply2->parent, 'userid' => (int) $discussion1reply2->userid, 'created' => $discussion1reply2->created, 'modified' => $discussion1reply2->modified, 'mailed' => $discussion1reply2->mailed, 'subject' => $discussion1reply2->subject, 'message' => file_rewrite_pluginfile_urls($discussion1reply2->message, 'pluginfile.php', $forum1context->id, 'mod_forum', 'post', $discussion1reply2->id), 'messageformat' => 1, 'messagetrust' => $discussion1reply2->messagetrust, 'attachment' => $discussion1reply2->attachment, 'totalscore' => $discussion1reply2->totalscore, 'mailnow' => $discussion1reply2->mailnow, 'children' => array(), 'canreply' => true, 'postread' => false, 'userfullname' => fullname($user3), 'userpictureurl' => $userpictureurl); $userpictureurl = moodle_url::make_webservice_pluginfile_url(context_user::instance($discussion1reply1->userid)->id, 'user', 'icon', null, '/', 'f1')->out(false); $expectedposts['posts'][] = array('id' => $discussion1reply1->id, 'discussion' => $discussion1reply1->discussion, 'parent' => $discussion1reply1->parent, 'userid' => (int) $discussion1reply1->userid, 'created' => $discussion1reply1->created, 'modified' => $discussion1reply1->modified, 'mailed' => $discussion1reply1->mailed, 'subject' => $discussion1reply1->subject, 'message' => file_rewrite_pluginfile_urls($discussion1reply1->message, 'pluginfile.php', $forum1context->id, 'mod_forum', 'post', $discussion1reply1->id), 'messageformat' => 1, 'messagetrust' => $discussion1reply1->messagetrust, 'attachment' => $discussion1reply1->attachment, 'totalscore' => $discussion1reply1->totalscore, 'mailnow' => $discussion1reply1->mailnow, 'children' => array($discussion1reply2->id), 'canreply' => true, 'postread' => false, 'userfullname' => fullname($user2), 'userpictureurl' => $userpictureurl); // Test a discussion with two additional posts (total 3 posts). $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC'); $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts); $this->assertEquals(3, count($posts['posts'])); // Unset the initial discussion post. array_pop($posts['posts']); $this->assertEquals($expectedposts, $posts); // Test discussion without additional posts. There should be only one post (the one created by the discussion). $posts = mod_forum_external::get_forum_discussion_posts($discussion2->id, 'modified', 'DESC'); $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts); $this->assertEquals(1, count($posts['posts'])); }
/** * Returns a list of forum discussions optionally sorted and paginated. * * @param int $forumid the forum instance id * @param string $sortby sort by this element (id, timemodified, timestart or timeend) * @param string $sortdirection sort direction: ASC or DESC * @param int $page page number * @param int $perpage items per page * * @return array the forum discussion details including warnings * @since Moodle 2.8 */ public static function get_forum_discussions_paginated($forumid, $sortby = 'timemodified', $sortdirection = 'DESC', $page = -1, $perpage = 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_paginated_parameters(), array('forumid' => $forumid, 'sortby' => $sortby, 'sortdirection' => $sortdirection, 'page' => $page, 'perpage' => $perpage)); // Compact/extract functions are not recommended. $forumid = $params['forumid']; $sortby = $params['sortby']; $sortdirection = $params['sortdirection']; $page = $params['page']; $perpage = $params['perpage']; $sortallowedvalues = array('id', 'timemodified', 'timestart', 'timeend'); if (!in_array($sortby, $sortallowedvalues)) { throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $sortby . '),' . 'allowed values are: ' . implode(',', $sortallowedvalues)); } $sortdirection = strtoupper($sortdirection); $directionallowedvalues = array('ASC', 'DESC'); if (!in_array($sortdirection, $directionallowedvalues)) { throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' . 'allowed values are: ' . implode(',', $directionallowedvalues)); } $forum = $DB->get_record('forum', array('id' => $forumid), '*', MUST_EXIST); $course = $DB->get_record('course', array('id' => $forum->course), '*', MUST_EXIST); $cm = get_coursemodule_from_instance('forum', $forum->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); // Check they have the view forum capability. require_capability('mod/forum:viewdiscussion', $modcontext, null, true, 'noviewdiscussionspermission', 'forum'); $sort = 'd.' . $sortby . ' ' . $sortdirection; $alldiscussions = forum_get_discussions($cm, $sort, true, -1, -1, true, $page, $perpage); if ($alldiscussions) { $canviewfullname = has_capability('moodle/site:viewfullnames', $modcontext); // Get the unreads array, this takes a forum id and returns data for all discussions. $unreads = array(); if ($cantrack = forum_tp_can_track_forums($forum)) { if ($forumtracked = forum_tp_is_tracked($forum)) { $unreads = forum_get_discussions_unread($cm); } } // The forum function returns the replies for all the discussions in a given forum. $replies = forum_count_discussion_replies($forumid, $sort, -1, $page, $perpage); foreach ($alldiscussions as $discussion) { // This function checks for qanda forums. // Note that the forum_get_discussions returns as id the post id, not the discussion id so we need to do this. $discussionrec = clone $discussion; $discussionrec->id = $discussion->discussion; if (!forum_user_can_see_discussion($forum, $discussionrec, $modcontext)) { $warning = array(); // Function forum_get_discussions returns forum_posts ids not forum_discussions ones. $warning['item'] = 'post'; $warning['itemid'] = $discussion->id; $warning['warningcode'] = '1'; $warning['message'] = 'You can\'t see this discussion'; $warnings[] = $warning; continue; } $discussion->numunread = 0; if ($cantrack && $forumtracked) { if (isset($unreads[$discussion->discussion])) { $discussion->numunread = (int) $unreads[$discussion->discussion]; } } $discussion->numreplies = 0; if (!empty($replies[$discussion->discussion])) { $discussion->numreplies = (int) $replies[$discussion->discussion]->replies; } // Load user objects from the results of the query. $user = new stdclass(); $user->id = $discussion->userid; $user = username_load_fields_from_object($user, $discussion); $discussion->userfullname = fullname($user, $canviewfullname); // We can have post written by users that are deleted. In this case, those users don't have a valid context. $usercontext = context_user::instance($user->id, IGNORE_MISSING); if ($usercontext) { $discussion->userpictureurl = moodle_url::make_webservice_pluginfile_url($usercontext->id, 'user', 'icon', null, '/', 'f1')->out(false); } else { $discussion->userpictureurl = ''; } $usermodified = new stdclass(); $usermodified->id = $discussion->usermodified; $usermodified = username_load_fields_from_object($usermodified, $discussion, 'um'); $discussion->usermodifiedfullname = fullname($usermodified, $canviewfullname); // We can have post written by users that are deleted. In this case, those users don't have a valid context. $usercontext = context_user::instance($usermodified->id, IGNORE_MISSING); if ($usercontext) { $discussion->usermodifiedpictureurl = moodle_url::make_webservice_pluginfile_url($usercontext->id, 'user', 'icon', null, '/', 'f1')->out(false); } else { $discussion->usermodifiedpictureurl = ''; } // Rewrite embedded images URLs. list($discussion->message, $discussion->messageformat) = external_format_text($discussion->message, $discussion->messageformat, $modcontext->id, 'mod_forum', 'post', $discussion->id); // List attachments. if (!empty($discussion->attachment)) { $discussion->attachments = array(); $fs = get_file_storage(); if ($files = $fs->get_area_files($modcontext->id, 'mod_forum', 'attachment', $discussion->id, "filename", false)) { foreach ($files as $file) { $filename = $file->get_filename(); $discussion->attachments[] = array('filename' => $filename, 'mimetype' => $file->get_mimetype(), 'fileurl' => file_encode_url($CFG->wwwroot . '/webservice/pluginfile.php', '/' . $modcontext->id . '/mod_forum/attachment/' . $discussion->id . '/' . $filename)); } } } $discussions[] = $discussion; } } $result = array(); $result['discussions'] = $discussions; $result['warnings'] = $warnings; return $result; }
/** * Search contacts. * * @param string $searchtext query string. * @param bool $onlymycourses limit the search to the user's courses only. * @return external_description * @since Moodle 2.5 */ public static function search_contacts($searchtext, $onlymycourses = false) { global $CFG, $USER; require_once $CFG->dirroot . '/user/lib.php'; // Check if messaging is enabled. if (!$CFG->messaging) { throw new moodle_exception('disabled', 'message'); } require_once $CFG->libdir . '/enrollib.php'; $params = array('searchtext' => $searchtext, 'onlymycourses' => $onlymycourses); $params = self::validate_parameters(self::search_contacts_parameters(), $params); // Extra validation, we do not allow empty queries. if ($params['searchtext'] === '') { throw new moodle_exception('querystringcannotbeempty'); } $courseids = array(); if ($params['onlymycourses']) { $mycourses = enrol_get_my_courses(array('id')); foreach ($mycourses as $mycourse) { $courseids[] = $mycourse->id; } } else { $courseids[] = SITEID; } // Retrieving the users matching the query. $users = message_search_users($courseids, $params['searchtext']); $results = array(); foreach ($users as $user) { $results[$user->id] = $user; } // Reorganising information. foreach ($results as &$user) { $newuser = array('id' => $user->id, 'fullname' => fullname($user)); // Avoid undefined property notice as phone not specified. $user->phone1 = null; $user->phone2 = null; $usercontextid = context_user::instance($user->id)->id; $newuser['profileimageurl'] = moodle_url::make_webservice_pluginfile_url($usercontextid, 'user', 'icon', null, '/', 'f1')->out(false); $newuser['profileimageurlsmall'] = moodle_url::make_webservice_pluginfile_url($usercontextid, 'user', 'icon', null, '/', 'f2')->out(false); $user = $newuser; } return $results; }
/** * Export imscp resource contents * * @param stdClass $cm Course module object * @param string $baseurl Base URL for file downloads * @return array of file content */ function imscp_export_contents($cm, $baseurl) { global $DB; $contents = array(); $context = context_module::instance($cm->id); $imscp = $DB->get_record('imscp', array('id' => $cm->instance), '*', MUST_EXIST); // We export the IMSCP structure as json encoded string. $structure = array(); $structure['type'] = 'content'; $structure['filename'] = 'structure'; $structure['filepath'] = '/'; $structure['filesize'] = 0; $structure['fileurl'] = null; $structure['timecreated'] = $imscp->timemodified; $structure['timemodified'] = $imscp->timemodified; $structure['content'] = json_encode(unserialize($imscp->structure)); $structure['sortorder'] = 0; $structure['userid'] = null; $structure['author'] = null; $structure['license'] = null; $contents[] = $structure; // Area files. $fs = get_file_storage(); $files = $fs->get_area_files($context->id, 'mod_imscp', 'content', $imscp->revision, 'id ASC', false); foreach ($files as $fileinfo) { $file = array(); $file['type'] = 'file'; $file['filename'] = $fileinfo->get_filename(); $file['filepath'] = $fileinfo->get_filepath(); $file['filesize'] = $fileinfo->get_filesize(); $file['fileurl'] = moodle_url::make_webservice_pluginfile_url($context->id, 'mod_imscp', 'content', $imscp->revision, $fileinfo->get_filepath(), $fileinfo->get_filename())->out(false); $file['timecreated'] = $fileinfo->get_timecreated(); $file['timemodified'] = $fileinfo->get_timemodified(); $file['sortorder'] = $fileinfo->get_sortorder(); $file['userid'] = $fileinfo->get_userid(); $file['author'] = $fileinfo->get_author(); $file['license'] = $fileinfo->get_license(); $contents[] = $file; } return $contents; }
/** * Returns the list of files from a specific subwiki. * * @param int $wikiid The wiki instance ID. * @param int $groupid The group ID. If not defined, use current group. * @param int $userid The user ID. If not defined, use current user. * @return array Containing a list of warnings and a list of files. * @since Moodle 3.1 * @throws moodle_exception */ public static function get_subwiki_files($wikiid, $groupid = -1, $userid = 0) { $returnedfiles = array(); $warnings = array(); $params = self::validate_parameters(self::get_subwiki_files_parameters(), array('wikiid' => $wikiid, 'groupid' => $groupid, 'userid' => $userid)); // Get wiki instance. if (!($wiki = wiki_get_wiki($params['wikiid']))) { throw new moodle_exception('incorrectwikiid', 'wiki'); } list($course, $cm) = get_course_and_cm_from_instance($wiki, 'wiki'); $context = context_module::instance($cm->id); self::validate_context($context); // Determine groupid and userid to use. list($groupid, $userid) = self::determine_group_and_user($cm, $wiki, $params['groupid'], $params['userid']); // Get subwiki and validate it. $subwiki = wiki_get_subwiki_by_group_and_user_with_validation($wiki, $groupid, $userid); // Get subwiki based on group and user. if ($subwiki === false) { throw new moodle_exception('cannotviewfiles', 'wiki'); } else { if ($subwiki->id != -1) { // The subwiki exists, let's get the files. $fs = get_file_storage(); if ($files = $fs->get_area_files($context->id, 'mod_wiki', 'attachments', $subwiki->id, 'filename', false)) { foreach ($files as $file) { $filename = $file->get_filename(); $fileurl = moodle_url::make_webservice_pluginfile_url($context->id, 'mod_wiki', 'attachments', $subwiki->id, '/', $filename); $returnedfiles[] = array('filename' => $filename, 'mimetype' => $file->get_mimetype(), 'fileurl' => $fileurl->out(false), 'filepath' => $file->get_filepath(), 'filesize' => $file->get_filesize(), 'timemodified' => $file->get_timemodified()); } } } } $result = array(); $result['files'] = $returnedfiles; $result['warnings'] = $warnings; return $result; }
/** * Return the course information that is public (visible by every one) * * @param course_in_list $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(course_in_list $course, $coursecontext) { static $categoriescache = array(); // Category information. if (!array_key_exists($course->category, $categoriescache)) { $categoriescache[$course->category] = coursecat::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']); } // 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) = external_format_text($course->summary, $course->summaryformat, $coursecontext->id, 'course', 'summary', null); $displayname = get_course_display_name_for_list($course); $coursereturns = array(); $coursereturns['id'] = $course->id; $coursereturns['fullname'] = external_format_string($course->fullname, $coursecontext->id); $coursereturns['displayname'] = external_format_string($displayname, $coursecontext->id); $coursereturns['shortname'] = external_format_string($course->shortname, $coursecontext->id); $coursereturns['categoryid'] = $course->category; $coursereturns['categoryname'] = $category == null ? '' : $category->name; $coursereturns['summary'] = $summary; $coursereturns['summaryformat'] = $summaryformat; $coursereturns['summaryfiles'] = external_util::get_area_files($coursecontext->id, 'course', 'summary', false, false); $coursereturns['overviewfiles'] = $files; $coursereturns['contacts'] = $coursecontacts; $coursereturns['enrollmentmethods'] = $enroltypes; return $coursereturns; }
/** * Returns all area files (optionally limited by itemid). * * @param int $contextid context ID * @param string $component component * @param string $filearea file area * @param int $itemid item ID or all files if not specified * @param bool $useitemidinurl wether to use the item id in the file URL (modules intro don't use it) * @return array of files, compatible with the external_files structure. * @since Moodle 3.2 */ public static function get_area_files($contextid, $component, $filearea, $itemid = false, $useitemidinurl = true) { $files = array(); $fs = get_file_storage(); if ($areafiles = $fs->get_area_files($contextid, $component, $filearea, $itemid, 'itemid, filepath, filename', false)) { foreach ($areafiles as $areafile) { $file = array(); $file['filename'] = $areafile->get_filename(); $file['filepath'] = $areafile->get_filepath(); $file['mimetype'] = $areafile->get_mimetype(); $file['filesize'] = $areafile->get_filesize(); $file['timemodified'] = $areafile->get_timemodified(); $fileitemid = $useitemidinurl ? $areafile->get_itemid() : null; $file['fileurl'] = moodle_url::make_webservice_pluginfile_url($contextid, $component, $filearea, $fileitemid, $areafile->get_filepath(), $areafile->get_filename())->out(false); $files[] = $file; } } return $files; }
public function test_mod_scorm_get_scorms_by_courses() { global $DB; $this->resetAfterTest(true); // Create users. $student = self::getDataGenerator()->create_user(); $teacher = self::getDataGenerator()->create_user(); // Set to the student user. self::setUser($student); // Create courses to add the modules. $course1 = self::getDataGenerator()->create_course(); $course2 = self::getDataGenerator()->create_course(); // First scorm. $record = new stdClass(); $record->introformat = FORMAT_HTML; $record->course = $course1->id; $record->hidetoc = 2; $record->displayattemptstatus = 2; $record->skipview = 2; $scorm1 = self::getDataGenerator()->create_module('scorm', $record); // Second scorm. $record = new stdClass(); $record->introformat = FORMAT_HTML; $record->course = $course2->id; $scorm2 = self::getDataGenerator()->create_module('scorm', $record); $studentrole = $DB->get_record('role', array('shortname' => 'student')); $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher')); // Users enrolments. $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual'); $this->getDataGenerator()->enrol_user($teacher->id, $course1->id, $teacherrole->id, 'manual'); // Execute real Moodle enrolment as we'll call unenrol() method on the instance later. $enrol = enrol_get_plugin('manual'); $enrolinstances = enrol_get_instances($course2->id, true); foreach ($enrolinstances as $courseenrolinstance) { if ($courseenrolinstance->enrol == "manual") { $instance2 = $courseenrolinstance; break; } } $enrol->enrol_user($instance2, $student->id, $studentrole->id); $returndescription = mod_scorm_external::get_scorms_by_courses_returns(); // Test open/close dates. $timenow = time(); $scorm1->timeopen = $timenow - DAYSECS; $scorm1->timeclose = $timenow - HOURSECS; $DB->update_record('scorm', $scorm1); $result = mod_scorm_external::get_scorms_by_courses(array($course1->id)); $result = external_api::clean_returnvalue($returndescription, $result); $this->assertCount(1, $result['warnings']); // Only 'id', 'coursemodule', 'course', 'name', 'intro', 'introformat', 'introfiles'. $this->assertCount(7, $result['scorms'][0]); $this->assertEquals('expired', $result['warnings'][0]['warningcode']); $scorm1->timeopen = $timenow + DAYSECS; $scorm1->timeclose = $scorm1->timeopen + DAYSECS; $DB->update_record('scorm', $scorm1); $result = mod_scorm_external::get_scorms_by_courses(array($course1->id)); $result = external_api::clean_returnvalue($returndescription, $result); $this->assertCount(1, $result['warnings']); // Only 'id', 'coursemodule', 'course', 'name', 'intro', 'introformat', 'introfiles'. $this->assertCount(7, $result['scorms'][0]); $this->assertEquals('notopenyet', $result['warnings'][0]['warningcode']); // Reset times. $scorm1->timeopen = 0; $scorm1->timeclose = 0; $DB->update_record('scorm', $scorm1); // Create what we expect to be returned when querying the two courses. // First for the student user. $expectedfields = array('id', 'coursemodule', 'course', 'name', 'intro', 'introformat', 'version', 'maxgrade', 'grademethod', 'whatgrade', 'maxattempt', 'forcecompleted', 'forcenewattempt', 'lastattemptlock', 'displayattemptstatus', 'displaycoursestructure', 'sha1hash', 'md5hash', 'revision', 'launch', 'skipview', 'hidebrowse', 'hidetoc', 'nav', 'navpositionleft', 'navpositiontop', 'auto', 'popup', 'width', 'height', 'timeopen', 'timeclose', 'displayactivityname', 'packagesize', 'packageurl', 'scormtype', 'reference'); // Add expected coursemodule and data. $scorm1->coursemodule = $scorm1->cmid; $scorm1->section = 0; $scorm1->visible = true; $scorm1->groupmode = 0; $scorm1->groupingid = 0; $scorm2->coursemodule = $scorm2->cmid; $scorm2->section = 0; $scorm2->visible = true; $scorm2->groupmode = 0; $scorm2->groupingid = 0; // SCORM size. The same package is used in both SCORMs. $scormcontext1 = context_module::instance($scorm1->cmid); $scormcontext2 = context_module::instance($scorm2->cmid); $fs = get_file_storage(); $packagefile = $fs->get_file($scormcontext1->id, 'mod_scorm', 'package', 0, '/', $scorm1->reference); $packagesize = $packagefile->get_filesize(); $packageurl1 = moodle_url::make_webservice_pluginfile_url($scormcontext1->id, 'mod_scorm', 'package', 0, '/', $scorm1->reference)->out(false); $packageurl2 = moodle_url::make_webservice_pluginfile_url($scormcontext2->id, 'mod_scorm', 'package', 0, '/', $scorm2->reference)->out(false); $scorm1->packagesize = $packagesize; $scorm1->packageurl = $packageurl1; $scorm2->packagesize = $packagesize; $scorm2->packageurl = $packageurl2; // Forced to boolean as it is returned as PARAM_BOOL. $protectpackages = (bool) get_config('scorm', 'protectpackagedownloads'); $expected1 = array('protectpackagedownloads' => $protectpackages); $expected2 = array('protectpackagedownloads' => $protectpackages); foreach ($expectedfields as $field) { // Since we return the fields used as boolean as PARAM_BOOL instead PARAM_INT we need to force casting here. // From the returned fields definition we obtain the type expected for the field. if (empty($returndescription->keys['scorms']->content->keys[$field]->type)) { continue; } $fieldtype = $returndescription->keys['scorms']->content->keys[$field]->type; if ($fieldtype == PARAM_BOOL) { $expected1[$field] = (bool) $scorm1->{$field}; $expected2[$field] = (bool) $scorm2->{$field}; } else { $expected1[$field] = $scorm1->{$field}; $expected2[$field] = $scorm2->{$field}; } } $expected1['introfiles'] = []; $expected2['introfiles'] = []; $expectedscorms = array(); $expectedscorms[] = $expected2; $expectedscorms[] = $expected1; // Call the external function passing course ids. $result = mod_scorm_external::get_scorms_by_courses(array($course2->id, $course1->id)); $result = external_api::clean_returnvalue($returndescription, $result); $this->assertEquals($expectedscorms, $result['scorms']); // Call the external function without passing course id. $result = mod_scorm_external::get_scorms_by_courses(); $result = external_api::clean_returnvalue($returndescription, $result); $this->assertEquals($expectedscorms, $result['scorms']); // Unenrol user from second course and alter expected scorms. $enrol->unenrol_user($instance2, $student->id); array_shift($expectedscorms); // Call the external function without passing course id. $result = mod_scorm_external::get_scorms_by_courses(); $result = external_api::clean_returnvalue($returndescription, $result); $this->assertEquals($expectedscorms, $result['scorms']); // Call for the second course we unenrolled the user from, expected warning. $result = mod_scorm_external::get_scorms_by_courses(array($course2->id)); $this->assertCount(1, $result['warnings']); $this->assertEquals('1', $result['warnings'][0]['warningcode']); $this->assertEquals($course2->id, $result['warnings'][0]['itemid']); // Now, try as a teacher for getting all the additional fields. self::setUser($teacher); $additionalfields = array('updatefreq', 'timemodified', 'options', 'completionstatusrequired', 'completionscorerequired', 'completionstatusallscos', 'autocommit', 'section', 'visible', 'groupmode', 'groupingid'); foreach ($additionalfields as $field) { $fieldtype = $returndescription->keys['scorms']->content->keys[$field]->type; if ($fieldtype == PARAM_BOOL) { $expectedscorms[0][$field] = (bool) $scorm1->{$field}; } else { $expectedscorms[0][$field] = $scorm1->{$field}; } } $result = mod_scorm_external::get_scorms_by_courses(); $result = external_api::clean_returnvalue($returndescription, $result); $this->assertEquals($expectedscorms, $result['scorms']); // Even with the SCORM closed in time teacher should retrieve the info. $scorm1->timeopen = $timenow - DAYSECS; $scorm1->timeclose = $timenow - HOURSECS; $DB->update_record('scorm', $scorm1); $expectedscorms[0]['timeopen'] = $scorm1->timeopen; $expectedscorms[0]['timeclose'] = $scorm1->timeclose; $result = mod_scorm_external::get_scorms_by_courses(); $result = external_api::clean_returnvalue($returndescription, $result); $this->assertEquals($expectedscorms, $result['scorms']); // Admin also should get all the information. self::setAdminUser(); $result = mod_scorm_external::get_scorms_by_courses(array($course1->id)); $result = external_api::clean_returnvalue($returndescription, $result); $this->assertEquals($expectedscorms, $result['scorms']); }
/** * Returns user's results for a specific choice * and a list of those users that did not answered yet. * * @param int $choiceid the choice instance id * @return array of responses details * @since Moodle 3.0 */ public static function get_choice_results($choiceid) { global $USER; $params = self::validate_parameters(self::get_choice_results_parameters(), array('choiceid' => $choiceid)); if (!$choice = choice_get_choice($params['choiceid'])) { throw new moodle_exception("invalidcoursemodule", "error"); } list($course, $cm) = get_course_and_cm_from_instance($choice, 'choice'); $context = context_module::instance($cm->id); self::validate_context($context); $groupmode = groups_get_activity_groupmode($cm); // Check if we have to include responses from inactive users. $onlyactive = $choice->includeinactive ? false : true; $users = choice_get_response_data($choice, $cm, $groupmode, $onlyactive); // Show those who haven't answered the question. if (!empty($choice->showunanswered)) { $choice->option[0] = get_string('notanswered', 'choice'); $choice->maxanswers[0] = 0; } $results = prepare_choice_show_results($choice, $course, $cm, $users); $options = array(); $fullnamecap = has_capability('moodle/site:viewfullnames', $context); foreach ($results->options as $optionid => $option) { $userresponses = array(); $numberofuser = 0; $percentageamount = 0; if (property_exists($option, 'user') and (has_capability('mod/choice:readresponses', $context) or choice_can_view_results($choice))) { $numberofuser = count($option->user); $percentageamount = ((float)$numberofuser / (float)$results->numberofuser) * 100.0; if ($choice->publish) { foreach ($option->user as $userresponse) { $response = array(); $response['userid'] = $userresponse->id; $response['fullname'] = fullname($userresponse, $fullnamecap); $usercontext = context_user::instance($userresponse->id, IGNORE_MISSING); if ($usercontext) { $profileimageurl = moodle_url::make_webservice_pluginfile_url($usercontext->id, 'user', 'icon', null, '/', 'f1')->out(false); } else { $profileimageurl = ''; } $response['profileimageurl'] = $profileimageurl; // Add optional properties. foreach (array('answerid', 'timemodified') as $field) { if (property_exists($userresponse, 'answerid')) { $response[$field] = $userresponse->$field; } } $userresponses[] = $response; } } } $options[] = array('id' => $optionid, 'text' => format_string($option->text, true, array('context' => $context)), 'maxanswer' => $option->maxanswer, 'userresponses' => $userresponses, 'numberofuser' => $numberofuser, 'percentageamount' => $percentageamount ); } $warnings = array(); return array( 'options' => $options, 'warnings' => $warnings ); }