/** * get_activity_tree :: course_modinfo -> integer -> context -> array * @param \course_modinfo $modinfo * @param integer $section_number * @param \context $context * @return array */ function get_activity_tree(\course_modinfo $modinfo, $section_number, \context $context) { $completion_info = new \completion_info($modinfo->get_course()); return F\map(F\filter($modinfo->get_section_info_all(), function (\section_info $section_info) { return $section_info->visible; }), function (\section_info $section_info) use($completion_info, $section_number, $context) { $mods = F\map(F\filter($section_info->modinfo->cms, function (\cm_info $cm_info) use($section_info) { global $CFG; return ($cm_info->uservisible || $CFG->enableavailability && !empty($cm_info->availableinfo)) && (int) $cm_info->sectionnum === (int) $section_info->section; }), function (\cm_info $cm_info) use($completion_info, $context) { global $CFG; $canComplete = $CFG->enablecompletion && isloggedin() && !isguestuser() && (int) $completion_info->is_enabled($cm_info) === COMPLETION_TRACKING_MANUAL; $hasCompleted = false; if ($canComplete) { $completion_data = $completion_info->get_data($cm_info, true); $hasCompleted = F\contains([COMPLETION_COMPLETE, COMPLETION_COMPLETE_PASS], (int) $completion_data->completionstate); } return (object) ['id' => (int) $cm_info->id, 'name' => $cm_info->name, 'modname' => $cm_info->modname, 'current' => is_current_mod($cm_info, $context), 'available' => $cm_info->available, 'canComplete' => $canComplete, 'hasCompleted' => $hasCompleted]; }); return (object) ['id' => (int) $section_info->id, 'section' => (int) $section_info->section, 'name' => \get_section_name($section_info->modinfo->courseid, $section_info->section), 'current' => is_current_section($section_info, $section_number, $context), 'activities' => array_values($mods)]; }); }
public function test_glossary_view() { global $CFG; $origcompletion = $CFG->enablecompletion; $CFG->enablecompletion = true; $this->resetAfterTest(true); // Generate all the things. $c1 = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); $g1 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id, 'completion' => COMPLETION_TRACKING_AUTOMATIC, 'completionview' => 1)); $g2 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id, 'completion' => COMPLETION_TRACKING_AUTOMATIC, 'completionview' => 1)); $u1 = $this->getDataGenerator()->create_user(); $this->getDataGenerator()->enrol_user($u1->id, $c1->id); $modinfo = course_modinfo::instance($c1->id); $cm1 = $modinfo->get_cm($g1->cmid); $cm2 = $modinfo->get_cm($g2->cmid); $ctx1 = $cm1->context; $completion = new completion_info($c1); $this->setUser($u1); // Confirm what we've set up. $this->assertEquals(COMPLETION_NOT_VIEWED, $completion->get_data($cm1, false, $u1->id)->viewed); $this->assertEquals(COMPLETION_INCOMPLETE, $completion->get_data($cm1, false, $u1->id)->completionstate); $this->assertEquals(COMPLETION_NOT_VIEWED, $completion->get_data($cm2, false, $u1->id)->viewed); $this->assertEquals(COMPLETION_INCOMPLETE, $completion->get_data($cm2, false, $u1->id)->completionstate); // Simulate the view call. $sink = $this->redirectEvents(); glossary_view($g1, $c1, $cm1, $ctx1, 'letter'); $events = $sink->get_events(); // Assertions. $this->assertCount(3, $events); $this->assertEquals('\\core\\event\\course_module_completion_updated', $events[0]->eventname); $this->assertEquals('\\core\\event\\course_module_completion_updated', $events[1]->eventname); $this->assertEquals('\\mod_glossary\\event\\course_module_viewed', $events[2]->eventname); $this->assertEquals($g1->id, $events[2]->objectid); $this->assertEquals('letter', $events[2]->other['mode']); $this->assertEquals(COMPLETION_VIEWED, $completion->get_data($cm1, false, $u1->id)->viewed); $this->assertEquals(COMPLETION_COMPLETE, $completion->get_data($cm1, false, $u1->id)->completionstate); $this->assertEquals(COMPLETION_NOT_VIEWED, $completion->get_data($cm2, false, $u1->id)->viewed); $this->assertEquals(COMPLETION_INCOMPLETE, $completion->get_data($cm2, false, $u1->id)->completionstate); // Tear down. $sink->close(); $CFG->enablecompletion = $origcompletion; }
/** * Test convect fetching. */ public function test_concept_fetching() { global $CFG, $DB; $this->resetAfterTest(true); $this->setAdminUser(); $CFG->glossary_linkbydefault = 1; $CFG->glossary_linkentries = 0; // Create a test courses. $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); $site = $DB->get_record('course', array('id' => SITEID)); // Create a glossary. $glossary1a = $this->getDataGenerator()->create_module('glossary', array('course' => $course1->id, 'mainglossary' => 1, 'usedynalink' => 1)); $glossary1b = $this->getDataGenerator()->create_module('glossary', array('course' => $course1->id, 'mainglossary' => 1, 'usedynalink' => 1)); $glossary1c = $this->getDataGenerator()->create_module('glossary', array('course' => $course1->id, 'mainglossary' => 1, 'usedynalink' => 0)); $glossary2 = $this->getDataGenerator()->create_module('glossary', array('course' => $course2->id, 'mainglossary' => 1, 'usedynalink' => 1)); $glossary3 = $this->getDataGenerator()->create_module('glossary', array('course' => $site->id, 'mainglossary' => 1, 'usedynalink' => 1, 'globalglossary' => 1)); /** @var mod_glossary_generator $generator */ $generator = $this->getDataGenerator()->get_plugin_generator('mod_glossary'); $entry1a1 = $generator->create_content($glossary1a, array('concept' => 'first', 'usedynalink' => 1), array('prvni', 'erste')); $entry1a2 = $generator->create_content($glossary1a, array('concept' => 'A&B', 'usedynalink' => 1)); $entry1a3 = $generator->create_content($glossary1a, array('concept' => 'neee', 'usedynalink' => 0)); $entry1b1 = $generator->create_content($glossary1b, array('concept' => 'second', 'usedynalink' => 1)); $entry1c1 = $generator->create_content($glossary1c, array('concept' => 'third', 'usedynalink' => 1)); $entry31 = $generator->create_content($glossary3, array('concept' => 'global', 'usedynalink' => 1), array('globalni')); $cat1 = $generator->create_category($glossary1a, array('name' => 'special'), array($entry1a1, $entry1a2)); \mod_glossary\local\concept_cache::reset_caches(); $concepts1 = \mod_glossary\local\concept_cache::get_concepts($course1->id); $this->assertCount(3, $concepts1[0]); $this->arrayHasKey($concepts1[0], $glossary1a->id); $this->arrayHasKey($concepts1[0], $glossary1b->id); $this->arrayHasKey($concepts1[0], $glossary3->id); $this->assertCount(3, $concepts1[1]); $this->arrayHasKey($concepts1[1], $glossary1a->id); $this->arrayHasKey($concepts1[1], $glossary1b->id); $this->arrayHasKey($concepts1[0], $glossary3->id); $this->assertCount(5, $concepts1[1][$glossary1a->id]); foreach ($concepts1[1][$glossary1a->id] as $concept) { $this->assertSame(array('id', 'glossaryid', 'concept', 'casesensitive', 'category', 'fullmatch'), array_keys((array) $concept)); if ($concept->concept === 'first') { $this->assertEquals($entry1a1->id, $concept->id); $this->assertEquals($glossary1a->id, $concept->glossaryid); $this->assertEquals(0, $concept->category); } else { if ($concept->concept === 'prvni') { $this->assertEquals($entry1a1->id, $concept->id); $this->assertEquals($glossary1a->id, $concept->glossaryid); $this->assertEquals(0, $concept->category); } else { if ($concept->concept === 'erste') { $this->assertEquals($entry1a1->id, $concept->id); $this->assertEquals($glossary1a->id, $concept->glossaryid); $this->assertEquals(0, $concept->category); } else { if ($concept->concept === 'A&B') { $this->assertEquals($entry1a2->id, $concept->id); $this->assertEquals($glossary1a->id, $concept->glossaryid); $this->assertEquals(0, $concept->category); } else { if ($concept->concept === 'special') { $this->assertEquals($cat1->id, $concept->id); $this->assertEquals($glossary1a->id, $concept->glossaryid); $this->assertEquals(1, $concept->category); } else { $this->fail('Unexpected concept: ' . $concept->concept); } } } } } } $this->assertCount(1, $concepts1[1][$glossary1b->id]); foreach ($concepts1[1][$glossary1b->id] as $concept) { $this->assertSame(array('id', 'glossaryid', 'concept', 'casesensitive', 'category', 'fullmatch'), array_keys((array) $concept)); if ($concept->concept === 'second') { $this->assertEquals($entry1b1->id, $concept->id); $this->assertEquals($glossary1b->id, $concept->glossaryid); $this->assertEquals(0, $concept->category); } else { $this->fail('Unexpected concept: ' . $concept->concept); } } $this->assertCount(2, $concepts1[1][$glossary3->id]); foreach ($concepts1[1][$glossary3->id] as $concept) { $this->assertSame(array('id', 'glossaryid', 'concept', 'casesensitive', 'category', 'fullmatch'), array_keys((array) $concept)); if ($concept->concept === 'global') { $this->assertEquals($entry31->id, $concept->id); $this->assertEquals($glossary3->id, $concept->glossaryid); $this->assertEquals(0, $concept->category); } else { if ($concept->concept === 'globalni') { $this->assertEquals($entry31->id, $concept->id); $this->assertEquals($glossary3->id, $concept->glossaryid); $this->assertEquals(0, $concept->category); } else { $this->fail('Unexpected concept: ' . $concept->concept); } } } $concepts3 = \mod_glossary\local\concept_cache::get_concepts($site->id); $this->assertCount(1, $concepts3[0]); $this->arrayHasKey($concepts3[0], $glossary3->id); $this->assertCount(1, $concepts3[1]); $this->arrayHasKey($concepts3[0], $glossary3->id); foreach ($concepts3[1][$glossary3->id] as $concept) { $this->assertSame(array('id', 'glossaryid', 'concept', 'casesensitive', 'category', 'fullmatch'), array_keys((array) $concept)); if ($concept->concept === 'global') { $this->assertEquals($entry31->id, $concept->id); $this->assertEquals($glossary3->id, $concept->glossaryid); $this->assertEquals(0, $concept->category); } else { if ($concept->concept === 'globalni') { $this->assertEquals($entry31->id, $concept->id); $this->assertEquals($glossary3->id, $concept->glossaryid); $this->assertEquals(0, $concept->category); } else { $this->fail('Unexpected concept: ' . $concept->concept); } } } $concepts2 = \mod_glossary\local\concept_cache::get_concepts($course2->id); $this->assertEquals($concepts3, $concepts2); // Test uservisible flag. set_config('enableavailability', 1); $glossary1d = $this->getDataGenerator()->create_module('glossary', array('course' => $course1->id, 'mainglossary' => 1, 'usedynalink' => 1, 'availability' => json_encode(\core_availability\tree::get_root_json(array(\availability_group\condition::get_json()))))); $entry1d1 = $generator->create_content($glossary1d, array('concept' => 'membersonly', 'usedynalink' => 1)); $user = $this->getDataGenerator()->create_user(); $this->getDataGenerator()->enrol_user($user->id, $course1->id); $this->getDataGenerator()->enrol_user($user->id, $course2->id); \mod_glossary\local\concept_cache::reset_caches(); $concepts1 = \mod_glossary\local\concept_cache::get_concepts($course1->id); $this->assertCount(4, $concepts1[0]); $this->assertCount(4, $concepts1[1]); $this->setUser($user); course_modinfo::clear_instance_cache(); \mod_glossary\local\concept_cache::reset_caches(); $concepts1 = \mod_glossary\local\concept_cache::get_concepts($course1->id); $this->assertCount(3, $concepts1[0]); $this->assertCount(3, $concepts1[1]); }
/** * Returns URL of a page that is supposed to contain detailed grade analysis * * At the moment, only activity modules are supported. The method generates link * to the module's file grade.php with the parameters id (cmid), itemid, itemnumber, * gradeid and userid. If the grade.php does not exist, null is returned. * * @return moodle_url|null URL or null if unable to construct it */ public function get_grade_analysis_url(grade_grade $grade) { global $CFG; /** @var array static cache of the grade.php file existence flags */ static $hasgradephp = array(); if (empty($grade->grade_item) or !$grade->grade_item instanceof grade_item) { throw new coding_exception('Passed grade without the associated grade item'); } $item = $grade->grade_item; if (!$item->is_external_item()) { // at the moment, only activity modules are supported return null; } if ($item->itemtype !== 'mod') { throw new coding_exception('Unknown external itemtype: ' . $item->itemtype); } if (empty($item->iteminstance) or empty($item->itemmodule) or empty($this->modinfo)) { return null; } if (!array_key_exists($item->itemmodule, $hasgradephp)) { if (file_exists($CFG->dirroot . '/mod/' . $item->itemmodule . '/grade.php')) { $hasgradephp[$item->itemmodule] = true; } else { $hasgradephp[$item->itemmodule] = false; } } if (!$hasgradephp[$item->itemmodule]) { return null; } $instances = $this->modinfo->get_instances(); if (empty($instances[$item->itemmodule][$item->iteminstance])) { return null; } $cm = $instances[$item->itemmodule][$item->iteminstance]; if (!$cm->uservisible) { return null; } $url = new moodle_url('/mod/' . $item->itemmodule . '/grade.php', array('id' => $cm->id, 'itemid' => $item->id, 'itemnumber' => $item->itemnumber, 'gradeid' => $grade->id, 'userid' => $grade->userid)); return $url; }
public function test_matching_cacherev() { global $DB, $CFG; $this->resetAfterTest(); $this->setAdminUser(); $cache = cache::make('core', 'coursemodinfo'); // Generate the course and pre-requisite module. $course = $this->getDataGenerator()->create_course(array('format' => 'topics', 'numsections' => 3), array('createsections' => true)); // Make sure the cacherev is set. $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertGreaterThan(0, $cacherev); $prevcacherev = $cacherev; // Reset course cache and make sure cacherev is bumped up but cache is empty. rebuild_course_cache($course->id, true); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertGreaterThan($prevcacherev, $cacherev); $this->assertEmpty($cache->get($course->id)); $prevcacherev = $cacherev; // Build course cache. Cacherev should not change but cache is now not empty. Make sure cacherev is the same everywhere. $modinfo = get_fast_modinfo($course->id); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertEquals($prevcacherev, $cacherev); $cachedvalue = $cache->get($course->id); $this->assertNotEmpty($cachedvalue); $this->assertEquals($cacherev, $cachedvalue->cacherev); $this->assertEquals($cacherev, $modinfo->get_course()->cacherev); $prevcacherev = $cacherev; // Little trick to check that cache is not rebuilt druing the next step - substitute the value in MUC and later check that it is still there. $cache->set($course->id, (object) array_merge((array) $cachedvalue, array('secretfield' => 1))); // Clear static cache and call get_fast_modinfo() again (pretend we are in another request). Cache should not be rebuilt. course_modinfo::clear_instance_cache(); $modinfo = get_fast_modinfo($course->id); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertEquals($prevcacherev, $cacherev); $cachedvalue = $cache->get($course->id); $this->assertNotEmpty($cachedvalue); $this->assertEquals($cacherev, $cachedvalue->cacherev); $this->assertNotEmpty($cachedvalue->secretfield); $this->assertEquals($cacherev, $modinfo->get_course()->cacherev); $prevcacherev = $cacherev; // Rebuild course cache. Cacherev must be incremented everywhere. rebuild_course_cache($course->id); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertGreaterThan($prevcacherev, $cacherev); $cachedvalue = $cache->get($course->id); $this->assertNotEmpty($cachedvalue); $this->assertEquals($cacherev, $cachedvalue->cacherev); $modinfo = get_fast_modinfo($course->id); $this->assertEquals($cacherev, $modinfo->get_course()->cacherev); $prevcacherev = $cacherev; // Update cacherev in DB and make sure the cache will be rebuilt on the next call to get_fast_modinfo(). increment_revision_number('course', 'cacherev', 'id = ?', array($course->id)); // We need to clear static cache for course_modinfo instances too. course_modinfo::clear_instance_cache(); $modinfo = get_fast_modinfo($course->id); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertGreaterThan($prevcacherev, $cacherev); $cachedvalue = $cache->get($course->id); $this->assertNotEmpty($cachedvalue); $this->assertEquals($cacherev, $cachedvalue->cacherev); $this->assertEquals($cacherev, $modinfo->get_course()->cacherev); $prevcacherev = $cacherev; // Reset cache for all courses and make sure this course cache is reset. rebuild_course_cache(0, true); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertGreaterThan($prevcacherev, $cacherev); $this->assertEmpty($cache->get($course->id)); // Rebuild again. $modinfo = get_fast_modinfo($course->id); $cachedvalue = $cache->get($course->id); $this->assertNotEmpty($cachedvalue); $this->assertEquals($cacherev, $cachedvalue->cacherev); $this->assertEquals($cacherev, $modinfo->get_course()->cacherev); $prevcacherev = $cacherev; // Purge all caches and make sure cacherev is increased and data from MUC erased. purge_all_caches(); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertGreaterThan($prevcacherev, $cacherev); $this->assertEmpty($cache->get($course->id)); }
/** * Render contents of main part of subpage * @param mod_subpage $subpage * @param course_modinfo $modinfo * @param array $sections * @param boolean $editing whether the user is allowed to edit this page * @param int $moveitem (currently not used) * @param boolean $movesection whether the user is allowed to move sections * @return string html for display */ public function render_subpage($subpage, $modinfo, $sections, $editing, $moveitem, $movesection) { global $PAGE, $OUTPUT, $CFG, $USER; $this->subpagecm = $subpage->get_course_module()->id; if (!empty($USER->activitycopy) && $movesection) { $content = $this->render_cancel_link($this->subpagecm); } else { $content = ''; } $content .= $this->render_intro($subpage); $streditsummary = get_string('editsummary'); $strdelete = get_string('delete'); if ($editing) { $strmoveup = get_string('moveup'); $strmovedown = get_string('movedown'); $strhide = get_string('hide'); $strshow = get_string('show'); $strstealth = get_string('stealth', 'subpage'); $strunstealth = get_string('unstealth', 'subpage'); } $coursecontext = get_context_instance(CONTEXT_COURSE, $subpage->get_course()->id); get_all_mods($subpage->get_course()->id, $mods, $modnames, $modnamesplural, $modnamesused); foreach ($mods as $modid => $unused) { if (!isset($modinfo->cms[$modid])) { rebuild_course_cache($subpage->get_course()->id); $modinfo =& get_fast_modinfo($subpage->get_course()); debugging('Rebuilding course cache', DEBUG_DEVELOPER); break; } } $lastpageorder = $subpage->get_last_section_pageorder(); if ($subpage->get_course()->format == 'weeks') { $content .= html_writer::start_tag('ul', array('class' => 'weeks')); } else { $content .= html_writer::start_tag('ul', array('class' => 'topics')); } foreach ($sections as $section) { // Check to see whether cms within the section are visible or not // If all cms are not visible then we don't show the section at all, // unless editing $visible = false; if ($section->sequence) { // get cm_info for this resources $instances = explode(',', $section->sequence); } else { $instances = array(); } foreach ($instances as $instance) { $cm = $modinfo->get_cm($instance); // check to see whether cm is visible if ($cm->uservisible) { $visible = true; break; } } // If section is empty so should be hidden, record that in object $section->autohide = !$visible; $content .= html_writer::start_tag('li', array('class' => 'section main clearfix', 'id' => 'section-' . $section->section)); $content .= html_writer::tag('div', ' ', array('class' => 'left side')); $content .= html_writer::start_tag('div', array('class' => 'right side')); if ($editing && has_capability('moodle/course:update', $coursecontext)) { // Show the hide/show eye if ($section->visible) { $content .= html_writer::start_tag('a', array('href' => 'view.php?id=' . $subpage->get_course_module()->id . '&hide=' . $section->section . '&sesskey=' . sesskey() . '#section-' . $section->id, 'title' => $strhide)); $content .= html_writer::empty_tag('img', array('src' => $OUTPUT->pix_url('i/hide'), 'class' => 'icon hide', 'alt' => $strhide)); $content .= html_writer::end_tag('a'); } else { $content .= html_writer::start_tag('a', array('href' => 'view.php?id=' . $subpage->get_course_module()->id . '&show=' . $section->section . '&sesskey=' . sesskey() . '#section-' . $section->id, 'title' => $strshow)); $content .= html_writer::empty_tag('img', array('src' => $OUTPUT->pix_url('i/show'), 'class' => 'icon show', 'alt' => $strshow)); $content .= html_writer::end_tag('a'); } // Show the stealth/unstealth section link if ($section->stealth) { $content .= html_writer::start_tag('form', array('method' => 'post', 'action' => 'stealth.php')); $content .= html_writer::start_tag('div'); $content .= html_writer::empty_tag('input', array('name' => 'id', 'value' => $subpage->get_course_module()->id, 'type' => 'hidden')); $content .= html_writer::empty_tag('input', array('name' => 'sesskey', 'value' => sesskey(), 'type' => 'hidden')); $content .= html_writer::empty_tag('input', array('name' => 'unstealth', 'value' => $section->id, 'type' => 'hidden')); $content .= html_writer::empty_tag('input', array('name' => 'icon', 'src' => $OUTPUT->pix_url('unstealth', 'mod_subpage'), 'type' => 'image', 'title' => $strunstealth, 'alt' => $strunstealth)); $content .= html_writer::end_tag('div'); $content .= html_writer::end_tag('form'); } else { $content .= html_writer::start_tag('form', array('method' => 'post', 'action' => 'stealth.php')); $content .= html_writer::start_tag('div'); $content .= html_writer::empty_tag('input', array('name' => 'id', 'value' => $subpage->get_course_module()->id, 'type' => 'hidden')); $content .= html_writer::empty_tag('input', array('name' => 'sesskey', 'value' => sesskey(), 'type' => 'hidden')); $content .= html_writer::empty_tag('input', array('name' => 'stealth', 'value' => $section->id, 'type' => 'hidden')); $content .= html_writer::empty_tag('input', array('name' => 'icon', 'src' => $OUTPUT->pix_url('stealth', 'mod_subpage'), 'type' => 'image', 'title' => $strstealth, 'alt' => $strstealth)); $content .= html_writer::end_tag('div'); $content .= html_writer::end_tag('form'); } $content .= html_writer::empty_tag('br', array()); if ($movesection) { $content .= html_writer::start_tag('span', array('class' => 'section_move_commands')); if ($section->pageorder > 1) { // Add a arrow to move section up $content .= html_writer::start_tag('a', array('href' => 'view.php?id=' . $subpage->get_course_module()->id . '&random=' . rand(1, 10000) . '§ion=' . $section->id . '&move=-1&sesskey=' . sesskey() . '#section-' . ($section->id - 1), 'title' => $strmoveup)); $content .= html_writer::empty_tag('img', array('src' => $OUTPUT->pix_url('t/up'), 'class' => 'icon up', 'alt' => $strmoveup)); $content .= html_writer::end_tag('a'); $content .= html_writer::empty_tag('br', array()); } if ($section->pageorder < $lastpageorder) { // Add an arrow to move section down $content .= html_writer::start_tag('a', array('href' => 'view.php?id=' . $subpage->get_course_module()->id . '&random=' . rand(1, 10000) . '§ion=' . $section->id . '&move=1&sesskey=' . sesskey() . '#section-' . ($section->id + 1), 'title' => $strmovedown)); $content .= html_writer::empty_tag('img', array('src' => $OUTPUT->pix_url('t/down'), 'class' => 'icon down', 'alt' => $strmovedown)); $content .= html_writer::end_tag('a'); $content .= html_writer::empty_tag('br', array()); } $content .= html_writer::end_tag('span'); } } $content .= html_writer::end_tag('div'); $content .= html_writer::start_tag('div', array('class' => 'content')); // Only show the section if visible and not stealthed or to users with permission if (($section->visible && !$section->stealth || has_capability('moodle/course:viewhiddensections', $coursecontext)) && ($editing || !$section->autohide)) { if ($section->stealth) { $content .= html_writer::start_tag('div', array('class' => 'stealthed')); } if (!empty($section->name)) { $content .= html_writer::tag('h3', format_string($section->name), array('class' => 'sectionname')); } $summary = ''; if ($section->summary) { $summarytext = file_rewrite_pluginfile_urls($section->summary, 'pluginfile.php', $coursecontext->id, 'course', 'section', $section->id); $summaryformatoptions = new stdClass(); $summaryformatoptions->noclean = true; $summaryformatoptions->overflowdiv = true; $summary .= format_text($summarytext, $section->summaryformat, $summaryformatoptions); } if ($editing && has_capability('moodle/course:update', $coursecontext)) { $summary .= html_writer::start_tag('a', array('href' => $CFG->wwwroot . '/course/editsection.php?id=' . $section->id . '&returnurl=' . urlencode($CFG->wwwroot . '/mod/subpage/view.php?id=' . $subpage->get_course_module()->id . '&recache=1&sesskey=' . sesskey()), 'title' => $streditsummary)); $summary .= html_writer::empty_tag('img', array('src' => $OUTPUT->pix_url('t/edit'), 'class' => 'icon edit', 'alt' => $streditsummary)); $summary .= html_writer::end_tag('a'); $summary .= html_writer::empty_tag('br', array()); $summary .= html_writer::start_tag('a', array('href' => $CFG->wwwroot . '/mod/subpage/view.php?id=' . $subpage->get_course_module()->id . '&delete=' . $section->id . '&sesskey=' . sesskey(), 'title' => $strdelete)); if (empty($section->sequence)) { $summary .= html_writer::empty_tag('img', array('src' => $OUTPUT->pix_url('t/delete'), 'class' => 'icon delete', 'alt' => $strdelete)); } $summary .= html_writer::end_tag('a'); $summary .= html_writer::empty_tag('br', array()); $summary .= html_writer::empty_tag('br', array()); } if ($summary !== '') { $content .= html_writer::tag('div', $summary, array('class' => 'summary')); } $content .= $this->render_section($subpage, $modinfo, $section, $editing, $moveitem, $mods, $modnamesused); if ($editing) { $content .= print_section_add_menus($subpage->get_course(), $section->section, $modnames, false, true); if (!empty($CFG->enablecourseajax) and $PAGE->theme->enablecourseajax) { // hacky way to add list to empty section to allow drag/drop into // empty sections $content = str_replace('</div><div class="section_add_menus">', '</div><ul class="section img-text"><li></li></ul>' . '<div class="section_add_menus">', $content); } } //now add returnto links to editing links: $pattern = '/mod.php\\?[A-Za-z0-9-&;=%:\\/\\-.]+/'; $content = preg_replace_callback($pattern, 'mod_subpage_renderer::subpage_url_regex', $content); if ($section->stealth) { $content .= html_writer::end_tag('div'); } } $content .= html_writer::end_tag('div'); //end of div class=content $content .= html_writer::end_tag('li'); } $content .= html_writer::end_tag('ul'); if ($editing) { $content .= $this->render_add_button($subpage); $content .= $this->render_bulkmove_buttons($subpage); } return $content; }
/** * Test get_calendar_events */ public function test_get_calendar_events() { global $DB, $USER; $this->resetAfterTest(true); $this->setAdminUser(); // Create a few stuff to test with. $user = $this->getDataGenerator()->create_user(); $course = $this->getDataGenerator()->create_course(); $record = new stdClass(); $record->courseid = $course->id; $group = $this->getDataGenerator()->create_group($record); $beforecount = $DB->count_records('event'); // Let's create a few events. $siteevent = $this->create_calendar_event('site', $USER->id, 'site'); $record = new stdClass(); $record->courseid = $course->id; $courseevent = $this->create_calendar_event('course', $USER->id, 'course', 2, time(), $record); $userevent = $this->create_calendar_event('user', $USER->id); $record = new stdClass(); $record->courseid = $course->id; $record->groupid = $group->id; $groupevent = $this->create_calendar_event('group', $USER->id, 'group', 0, time(), $record); $paramevents = array('eventids' => array($siteevent->id), 'courseids' => array($course->id), 'groupids' => array($group->id)); $options = array('siteevents' => true, 'userevents' => true); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); // Check to see if we got all events. $this->assertEquals(5, count($events['events'])); $this->assertEquals(0, count($events['warnings'])); $options = array('siteevents' => true, 'userevents' => true, 'timeend' => time() + 7 * WEEKSECS); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(5, count($events['events'])); $this->assertEquals(0, count($events['warnings'])); // Let's play around with caps. $this->setUser($user); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(2, count($events['events'])); // site, user. $this->assertEquals(2, count($events['warnings'])); // course, group. $role = $DB->get_record('role', array('shortname' => 'student')); $this->getDataGenerator()->enrol_user($user->id, $course->id, $role->id); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(4, count($events['events'])); // site, user, both course events. $this->assertEquals(1, count($events['warnings'])); // group. $options = array('siteevents' => true, 'userevents' => true, 'timeend' => time() + HOURSECS); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(3, count($events['events'])); // site, user, one course event. $this->assertEquals(1, count($events['warnings'])); // group. groups_add_member($group, $user); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(4, count($events['events'])); // site, user, group, one course event. $this->assertEquals(0, count($events['warnings'])); $paramevents = array('courseids' => array($course->id), 'groupids' => array($group->id)); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(4, count($events['events'])); // site, user, group, one course event. $this->assertEquals(0, count($events['warnings'])); $paramevents = array('groupids' => array($group->id, 23)); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(3, count($events['events'])); // site, user, group. $this->assertEquals(1, count($events['warnings'])); $paramevents = array('courseids' => array(23)); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(2, count($events['events'])); // site, user. $this->assertEquals(1, count($events['warnings'])); $paramevents = array(); $options = array('siteevents' => false, 'userevents' => false, 'timeend' => time() + 7 * WEEKSECS); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(0, count($events['events'])); // nothing returned. $this->assertEquals(0, count($events['warnings'])); $paramevents = array('eventids' => array($siteevent->id, $groupevent->id)); $options = array('siteevents' => false, 'userevents' => false, 'timeend' => time() + 7 * WEEKSECS); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(2, count($events['events'])); // site, group. $this->assertEquals(0, count($events['warnings'])); $paramevents = array('eventids' => array($siteevent->id)); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(1, count($events['events'])); // site. $this->assertEquals(0, count($events['warnings'])); // Try getting a course event by its id. $paramevents = array('eventids' => array($courseevent->id)); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(1, count($events['events'])); $this->assertEquals(0, count($events['warnings'])); // Now, create an activity event. $this->setAdminUser(); $nexttime = time() + DAYSECS; $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id, 'duedate' => $nexttime)); $this->setUser($user); $paramevents = array('courseids' => array($course->id)); $options = array('siteevents' => true, 'userevents' => true, 'timeend' => time() + WEEKSECS); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertCount(5, $events['events']); // Hide the assignment. set_coursemodule_visible($assign->cmid, 0); // Empty all the caches that may be affected by this change. accesslib_clear_all_caches_for_unit_testing(); course_modinfo::clear_instance_cache(); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); // Expect one less. $this->assertCount(4, $events['events']); }
/** * Works out whether activity is visible *for current user* - if this is false, they * aren't allowed to access it. * @return void */ private function update_user_visible() { global $CFG; $modcontext = get_context_instance(CONTEXT_MODULE, $this->id); $userid = $this->modinfo->get_user_id(); $this->uservisible = true; if ((!$this->visible or !$this->available) and !has_capability('moodle/course:viewhiddenactivities', $modcontext, $userid)) { // If the activity is hidden or unavailable, and you don't have viewhiddenactivities, // set it so that user can't see or access it $this->uservisible = false; } else { if (!empty($CFG->enablegroupmembersonly) and !empty($this->groupmembersonly) and !has_capability('moodle/site:accessallgroups', $modcontext, $userid)) { // If the activity has 'group members only' and you don't have accessallgroups... $groups = $this->modinfo->get_groups($this->groupingid); if (empty($groups)) { // ...and you don't belong to a group, then set it so you can't see/access it $this->uservisible = false; } } } }
/** * Gets the data required to fill the test plan template with the database contents. * * @param int $targetcourseid The target course id * @return stdClass The ids required by the test plan */ protected static function get_course_test_data($targetcourseid) { global $DB, $USER; $data = new stdClass(); // Getting course contents info as the current user (will be an admin). $course = new stdClass(); $course->id = $targetcourseid; $courseinfo = new course_modinfo($course, $USER->id); // Getting the first page module instance. if (!($pages = $courseinfo->get_instances_of('page'))) { print_error('error_nopageinstances', 'tool_generator'); } $data->pageid = reset($pages)->id; // Getting the first forum module instance and it's first discussion and reply as well. if (!($forums = $courseinfo->get_instances_of('forum'))) { print_error('error_noforuminstances', 'tool_generator'); } $forum = reset($forums); // Getting the first discussion (and reply). if (!($discussions = forum_get_discussions($forum, 'd.timemodified ASC', false, -1, 1))) { print_error('error_noforumdiscussions', 'tool_generator'); } $discussion = reset($discussions); $data->forumid = $forum->id; $data->forumdiscussionid = $discussion->discussion; $data->forumreplyid = $discussion->id; // According to the current test plan. return $data; }
/** * Return an array of modules that can be moved in this situation * The Array is keyed first with sections (subpage or main course) * and then the modules within each section by cmid * @param Object $subpage current subpage * @param Array $allsubpages * @param Array $coursesections * @param Object $modinfo * @param String $move to or from * @return Array mixed */ public static function moveable_modules($subpage, $allsubpages, $coursesections, course_modinfo $modinfo, $move) { global $OUTPUT; get_all_mods($subpage->get_course()->id, $allmods, $modnames, $modnamesplural, $modnamesused); $mods = array(); $subsections = array(); if (!empty($allsubpages) && $move === 'to') { foreach ($allsubpages as $sub) { $subsections += $sub->get_sections(); } $sections = $coursesections; } else { $subsections = $subpage->get_sections(); $sections = $subsections; } if ($sections) { foreach ($sections as $section) { if (!empty($section->sequence)) { if ($move === 'to' && array_key_exists($section->id, $subsections)) { continue; } $sectionalt = isset($section->pageorder) ? $section->pageorder : $section->section; if ($move === 'to') { // Include the required course/format library. global $CFG; require_once "{$CFG->dirroot}/course/format/" . $subpage->get_course()->format . "/lib.php"; $callbackfunction = 'callback_' . $subpage->get_course()->format . '_get_section_name'; if (function_exists($callbackfunction)) { $name = $callbackfunction($subpage->get_course(), $section); } else { $name = $section->name ? $section->name : get_string('section') . ' ' . $sectionalt; } } else { $name = $section->name ? $section->name : get_string('section') . ' ' . $sectionalt; } $sectionmods = explode(',', $section->sequence); foreach ($sectionmods as $modnumber) { if (empty($allmods[$modnumber]) || $modnumber === $subpage->get_course_module()->id) { continue; } $instancename = format_string($modinfo->cms[$modnumber]->name, true, $subpage->get_course()->id); $icon = $modinfo->get_cm($modnumber)->get_icon_url(); $mod = $allmods[$modnumber]; $mods[$section->section]['section'] = $name; $mods[$section->section]['pageorder'] = $sectionalt; $mods[$section->section]['mods'][$modnumber] = "<span><img src='{$icon}' /> " . $instancename . "</span>"; } } } } return $mods; }
private function get_activity_link($element) { global $CFG; $itemtype = $element['object']->itemtype; $itemmodule = $element['object']->itemmodule; $iteminstance = $element['object']->iteminstance; // Links only for module items that have valid instance, module and are // called from grade_tree with valid modinfo if ($itemtype != 'mod' || !$iteminstance || !$itemmodule || !$this->modinfo) { return null; } // Get $cm efficiently and with visibility information using modinfo $instances = $this->modinfo->get_instances(); if (empty($instances[$itemmodule][$iteminstance])) { return null; } $cm = $instances[$itemmodule][$iteminstance]; // Do not add link if activity is not visible to the current user if (!$cm->uservisible) { return null; } // If module has grade.php, link to that, otherwise view.php $dir = $CFG->dirroot . '/mod/' . $itemmodule; if (file_exists($dir . '/grade.php')) { return new moodle_url('/mod/' . $itemmodule . '/grade.php', array('id' => $cm->id)); } else { return new moodle_url('/mod/' . $itemmodule . '/view.php', array('id' => $cm->id)); } }
/** * Test start_attempt */ public function test_start_attempt() { global $DB; // Create a new quiz with attempts. $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); $data = array('course' => $this->course->id, 'sumgrades' => 1); $quiz = $quizgenerator->create_instance($data); $context = context_module::instance($quiz->cmid); try { mod_quiz_external::start_attempt($quiz->id); $this->fail('Exception expected due to missing questions.'); } catch (moodle_quiz_exception $e) { $this->assertEquals('noquestionsfound', $e->errorcode); } // Create a question. $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); $cat = $questiongenerator->create_question_category(); $question = $questiongenerator->create_question('numerical', null, array('category' => $cat->id)); quiz_add_quiz_question($question->id, $quiz); $quizobj = quiz::create($quiz->id, $this->student->id); // Set grade to pass. $item = grade_item::fetch(array('courseid' => $this->course->id, 'itemtype' => 'mod', 'itemmodule' => 'quiz', 'iteminstance' => $quiz->id, 'outcomeid' => null)); $item->gradepass = 80; $item->update(); $this->setUser($this->student); // Try to open attempt in closed quiz. $quiz->timeopen = time() - WEEKSECS; $quiz->timeclose = time() - DAYSECS; $DB->update_record('quiz', $quiz); $result = mod_quiz_external::start_attempt($quiz->id); $result = external_api::clean_returnvalue(mod_quiz_external::start_attempt_returns(), $result); $this->assertEquals([], $result['attempt']); $this->assertCount(1, $result['warnings']); // Now with a password. $quiz->timeopen = 0; $quiz->timeclose = 0; $quiz->password = '******'; $DB->update_record('quiz', $quiz); try { mod_quiz_external::start_attempt($quiz->id, array(array("name" => "quizpassword", "value" => 'bad'))); $this->fail('Exception expected due to invalid passwod.'); } catch (moodle_exception $e) { $this->assertEquals(get_string('passworderror', 'quizaccess_password'), $e->errorcode); } // Now, try everything correct. $result = mod_quiz_external::start_attempt($quiz->id, array(array("name" => "quizpassword", "value" => 'abc'))); $result = external_api::clean_returnvalue(mod_quiz_external::start_attempt_returns(), $result); $this->assertEquals(1, $result['attempt']['attempt']); $this->assertEquals($this->student->id, $result['attempt']['userid']); $this->assertEquals($quiz->id, $result['attempt']['quiz']); $this->assertCount(0, $result['warnings']); $attemptid = $result['attempt']['id']; // We are good, try to start a new attempt now. try { mod_quiz_external::start_attempt($quiz->id, array(array("name" => "quizpassword", "value" => 'abc'))); $this->fail('Exception expected due to attempt not finished.'); } catch (moodle_quiz_exception $e) { $this->assertEquals('attemptstillinprogress', $e->errorcode); } // Finish the started attempt. // Process some responses from the student. $timenow = time(); $attemptobj = quiz_attempt::create($attemptid); $tosubmit = array(1 => array('answer' => '3.14')); $attemptobj->process_submitted_actions($timenow, false, $tosubmit); // Finish the attempt. $attemptobj = quiz_attempt::create($attemptid); $this->assertTrue($attemptobj->has_response_to_at_least_one_graded_question()); $attemptobj->process_finish($timenow, false); // We should be able to start a new attempt. $result = mod_quiz_external::start_attempt($quiz->id, array(array("name" => "quizpassword", "value" => 'abc'))); $result = external_api::clean_returnvalue(mod_quiz_external::start_attempt_returns(), $result); $this->assertEquals(2, $result['attempt']['attempt']); $this->assertEquals($this->student->id, $result['attempt']['userid']); $this->assertEquals($quiz->id, $result['attempt']['quiz']); $this->assertCount(0, $result['warnings']); // Test user with no capabilities. // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles. assign_capability('mod/quiz:attempt', CAP_PROHIBIT, $this->studentrole->id, $context->id); // Empty all the caches that may be affected by this change. accesslib_clear_all_caches_for_unit_testing(); course_modinfo::clear_instance_cache(); try { mod_quiz_external::start_attempt($quiz->id); $this->fail('Exception expected due to missing capability.'); } catch (required_capability_exception $e) { $this->assertEquals('nopermissions', $e->errorcode); } }
/** * Fill the table with data. * * @param $element - An array containing the table data for the current row. */ private function fill_table_recursive(&$element) { global $DB, $CFG; $type = $element['type']; $depth = $element['depth']; $grade_object = $element['object']; $eid = $grade_object->id; $element['userid'] = $this->user->id; $fullname = $this->gtree->get_element_header($element, true, true, true, true, true); $data = array(); $hidden = ''; $excluded = ''; $itemlevel = ($type == 'categoryitem' || $type == 'category' || $type == 'courseitem') ? $depth : ($depth + 1); $class = 'level' . $itemlevel . ' level' . ($itemlevel % 2 ? 'odd' : 'even'); $classfeedback = ''; // If this is a hidden grade category, hide it completely from the user if ($type == 'category' && $grade_object->is_hidden() && !$this->canviewhidden && ( $this->showhiddenitems == GRADE_REPORT_USER_HIDE_HIDDEN || ($this->showhiddenitems == GRADE_REPORT_USER_HIDE_UNTIL && !$grade_object->is_hiddenuntil()))) { return false; } if ($type == 'category') { $this->evenodd[$depth] = (($this->evenodd[$depth] + 1) % 2); } $alter = ($this->evenodd[$depth] == 0) ? 'even' : 'odd'; /// Process those items that have scores associated if ($type == 'item' or $type == 'categoryitem' or $type == 'courseitem') { $header_row = "row_{$eid}_{$this->user->id}"; $header_cat = "cat_{$grade_object->categoryid}_{$this->user->id}"; if (! $grade_grade = grade_grade::fetch(array('itemid'=>$grade_object->id,'userid'=>$this->user->id))) { $grade_grade = new grade_grade(); $grade_grade->userid = $this->user->id; $grade_grade->itemid = $grade_object->id; } $grade_grade->load_grade_item(); /// Hidden Items if ($grade_grade->grade_item->is_hidden()) { $hidden = ' dimmed_text'; } $hide = false; // If this is a hidden grade item, hide it completely from the user. if ($grade_grade->is_hidden() && !$this->canviewhidden && ( $this->showhiddenitems == GRADE_REPORT_USER_HIDE_HIDDEN || ($this->showhiddenitems == GRADE_REPORT_USER_HIDE_UNTIL && !$grade_grade->is_hiddenuntil()))) { $hide = true; } else if (!empty($grade_object->itemmodule) && !empty($grade_object->iteminstance)) { // The grade object can be marked visible but still be hidden if // the student cannot see the activity due to conditional access // and it's set to be hidden entirely. $instances = $this->modinfo->get_instances_of($grade_object->itemmodule); if (!empty($instances[$grade_object->iteminstance])) { $cm = $instances[$grade_object->iteminstance]; if (!$cm->uservisible) { // If there is 'availableinfo' text then it is only greyed // out and not entirely hidden. if (!$cm->availableinfo) { $hide = true; } } } } // Actual Grade - We need to calculate this whether the row is hidden or not. $gradeval = $grade_grade->finalgrade; $hint = $grade_grade->get_aggregation_hint(); if (!$this->canviewhidden) { /// Virtual Grade (may be calculated excluding hidden items etc). $adjustedgrade = $this->blank_hidden_total_and_adjust_bounds($this->courseid, $grade_grade->grade_item, $gradeval); $gradeval = $adjustedgrade['grade']; // We temporarily adjust the view of this grade item - because the min and // max are affected by the hidden values in the aggregation. $grade_grade->grade_item->grademax = $adjustedgrade['grademax']; $grade_grade->grade_item->grademin = $adjustedgrade['grademin']; $hint['status'] = $adjustedgrade['aggregationstatus']; $hint['weight'] = $adjustedgrade['aggregationweight']; } else { // The max and min for an aggregation may be different to the grade_item. if (!is_null($gradeval)) { $grade_grade->grade_item->grademax = $grade_grade->rawgrademax; $grade_grade->grade_item->grademin = $grade_grade->rawgrademin; } } if (!$hide) { /// Excluded Item /** if ($grade_grade->is_excluded()) { $fullname .= ' ['.get_string('excluded', 'grades').']'; $excluded = ' excluded'; } **/ /// Other class information $class .= $hidden . $excluded; if ($this->switch) { // alter style based on whether aggregation is first or last $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " ".$alter."d$depth baggt b2b" : " item b1b"; } else { $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " ".$alter."d$depth baggb" : " item b1b"; } if ($type == 'categoryitem' or $type == 'courseitem') { $header_cat = "cat_{$grade_object->iteminstance}_{$this->user->id}"; } /// Name $data['itemname']['content'] = $fullname; $data['itemname']['class'] = $class; $data['itemname']['colspan'] = ($this->maxdepth - $depth); $data['itemname']['celltype'] = 'th'; $data['itemname']['id'] = $header_row; if ($this->showfeedback) { // Copy $class before appending itemcenter as feedback should not be centered $classfeedback = $class; } $class .= " itemcenter "; if ($this->showweight) { $data['weight']['class'] = $class; $data['weight']['content'] = '-'; $data['weight']['headers'] = "$header_cat $header_row weight"; // has a weight assigned, might be extra credit // This obliterates the weight because it provides a more informative description. if (is_numeric($hint['weight'])) { $data['weight']['content'] = format_float($hint['weight'] * 100.0, 2) . ' %'; } if ($hint['status'] != 'used' && $hint['status'] != 'unknown') { $data['weight']['content'] .= '<br>' . get_string('aggregationhint' . $hint['status'], 'grades'); } } if ($this->showgrade) { if ($grade_grade->grade_item->needsupdate) { $data['grade']['class'] = $class.' gradingerror'; $data['grade']['content'] = get_string('error'); } else if (!empty($CFG->grade_hiddenasdate) and $grade_grade->get_datesubmitted() and !$this->canviewhidden and $grade_grade->is_hidden() and !$grade_grade->grade_item->is_category_item() and !$grade_grade->grade_item->is_course_item()) { // the problem here is that we do not have the time when grade value was modified, 'timemodified' is general modification date for grade_grades records $class .= ' datesubmitted'; $data['grade']['class'] = $class; $data['grade']['content'] = get_string('submittedon', 'grades', userdate($grade_grade->get_datesubmitted(), get_string('strftimedatetimeshort'))); } else if ($grade_grade->is_hidden()) { $data['grade']['class'] = $class.' dimmed_text'; $data['grade']['content'] = '-'; if ($this->canviewhidden) { $data['grade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true); } } else { $data['grade']['class'] = $class; $data['grade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true); } $data['grade']['headers'] = "$header_cat $header_row grade"; } // Range if ($this->showrange) { $data['range']['class'] = $class; $data['range']['content'] = $grade_grade->grade_item->get_formatted_range(GRADE_DISPLAY_TYPE_REAL, $this->rangedecimals); $data['range']['headers'] = "$header_cat $header_row range"; } // Percentage if ($this->showpercentage) { if ($grade_grade->grade_item->needsupdate) { $data['percentage']['class'] = $class.' gradingerror'; $data['percentage']['content'] = get_string('error'); } else if ($grade_grade->is_hidden()) { $data['percentage']['class'] = $class.' dimmed_text'; $data['percentage']['content'] = '-'; if ($this->canviewhidden) { $data['percentage']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE); } } else { $data['percentage']['class'] = $class; $data['percentage']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE); } $data['percentage']['headers'] = "$header_cat $header_row percentage"; } // Lettergrade if ($this->showlettergrade) { if ($grade_grade->grade_item->needsupdate) { $data['lettergrade']['class'] = $class.' gradingerror'; $data['lettergrade']['content'] = get_string('error'); } else if ($grade_grade->is_hidden()) { $data['lettergrade']['class'] = $class.' dimmed_text'; if (!$this->canviewhidden) { $data['lettergrade']['content'] = '-'; } else { $data['lettergrade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_LETTER); } } else { $data['lettergrade']['class'] = $class; $data['lettergrade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_LETTER); } $data['lettergrade']['headers'] = "$header_cat $header_row lettergrade"; } // Rank if ($this->showrank) { if ($grade_grade->grade_item->needsupdate) { $data['rank']['class'] = $class.' gradingerror'; $data['rank']['content'] = get_string('error'); } elseif ($grade_grade->is_hidden()) { $data['rank']['class'] = $class.' dimmed_text'; $data['rank']['content'] = '-'; } else if (is_null($gradeval)) { // no grade, no rank $data['rank']['class'] = $class; $data['rank']['content'] = '-'; } else { /// find the number of users with a higher grade $sql = "SELECT COUNT(DISTINCT(userid)) FROM {grade_grades} WHERE finalgrade > ? AND itemid = ? AND hidden = 0"; $rank = $DB->count_records_sql($sql, array($grade_grade->finalgrade, $grade_grade->grade_item->id)) + 1; $data['rank']['class'] = $class; $data['rank']['content'] = "$rank/".$this->get_numusers(false); // total course users } $data['rank']['headers'] = "$header_cat $header_row rank"; } // Average if ($this->showaverage) { $data['average']['class'] = $class; if (!empty($this->gtree->items[$eid]->avg)) { $data['average']['content'] = $this->gtree->items[$eid]->avg; } else { $data['average']['content'] = '-'; } $data['average']['headers'] = "$header_cat $header_row average"; } // Feedback if ($this->showfeedback) { if ($grade_grade->overridden > 0 AND ($type == 'categoryitem' OR $type == 'courseitem')) { $data['feedback']['class'] = $classfeedback.' feedbacktext'; $data['feedback']['content'] = get_string('overridden', 'grades').': ' . format_text($grade_grade->feedback, $grade_grade->feedbackformat); } else if (empty($grade_grade->feedback) or (!$this->canviewhidden and $grade_grade->is_hidden())) { $data['feedback']['class'] = $classfeedback.' feedbacktext'; $data['feedback']['content'] = ' '; } else { $data['feedback']['class'] = $classfeedback.' feedbacktext'; $data['feedback']['content'] = format_text($grade_grade->feedback, $grade_grade->feedbackformat); } $data['feedback']['headers'] = "$header_cat $header_row feedback"; } // Contribution to the course total column. if ($this->showcontributiontocoursetotal) { $data['contributiontocoursetotal']['class'] = $class; $data['contributiontocoursetotal']['content'] = '-'; $data['contributiontocoursetotal']['headers'] = "$header_cat $header_row contributiontocoursetotal"; } } // We collect the aggregation hints whether they are hidden or not. if ($this->showcontributiontocoursetotal) { $hint['grademax'] = $grade_grade->grade_item->grademax; $hint['grademin'] = $grade_grade->grade_item->grademin; $hint['grade'] = $gradeval; $parent = $grade_object->load_parent_category(); if ($grade_object->is_category_item()) { $parent = $parent->load_parent_category(); } $hint['parent'] = $parent->load_grade_item()->id; $this->aggregationhints[$grade_grade->itemid] = $hint; } } /// Category if ($type == 'category') { $data['leader']['class'] = $class.' '.$alter."d$depth b1t b2b b1l"; $data['leader']['rowspan'] = $element['rowspan']; if ($this->switch) { // alter style based on whether aggregation is first or last $data['itemname']['class'] = $class.' '.$alter."d$depth b1b b1t"; } else { $data['itemname']['class'] = $class.' '.$alter."d$depth b2t"; } $data['itemname']['colspan'] = ($this->maxdepth - $depth + count($this->tablecolumns) - 1); $data['itemname']['content'] = $fullname; $data['itemname']['celltype'] = 'th'; $data['itemname']['id'] = "cat_{$grade_object->id}_{$this->user->id}"; } /// Add this row to the overall system foreach ($data as $key => $celldata) { $data[$key]['class'] .= ' column-' . $key; } $this->tabledata[] = $data; /// Recursively iterate through all child elements if (isset($element['children'])) { foreach ($element['children'] as $key=>$child) { $this->fill_table_recursive($element['children'][$key]); } } // Check we are showing this column, and we are looking at the root of the table. // This should be the very last thing this fill_table_recursive function does. if ($this->showcontributiontocoursetotal && ($type == 'category' && $depth == 1)) { // We should have collected all the hints by now - walk the tree again and build the contributions column. $this->fill_contributions_column($element); } }
/** * Test test_view_submission_status */ public function test_view_submission_status() { global $DB; $this->resetAfterTest(true); $this->setAdminUser(); // Setup test data. $course = $this->getDataGenerator()->create_course(); $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id)); $context = context_module::instance($assign->cmid); $cm = get_coursemodule_from_instance('assign', $assign->id); // Test invalid instance id. try { mod_assign_external::view_submission_status(0); $this->fail('Exception expected due to invalid mod_assign instance id.'); } catch (moodle_exception $e) { $this->assertEquals('invalidrecord', $e->errorcode); } // Test not-enrolled user. $user = self::getDataGenerator()->create_user(); $this->setUser($user); try { mod_assign_external::view_submission_status($assign->id); $this->fail('Exception expected due to not enrolled user.'); } catch (moodle_exception $e) { $this->assertEquals('requireloginerror', $e->errorcode); } // Test user with full capabilities. $studentrole = $DB->get_record('role', array('shortname' => 'student')); $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id); // Trigger and capture the event. $sink = $this->redirectEvents(); $result = mod_assign_external::view_submission_status($assign->id); $result = external_api::clean_returnvalue(mod_assign_external::view_submission_status_returns(), $result); $events = $sink->get_events(); $this->assertCount(1, $events); $event = array_shift($events); // Checking that the event contains the expected values. $this->assertInstanceOf('\\mod_assign\\event\\submission_status_viewed', $event); $this->assertEquals($context, $event->get_context()); $moodleurl = new \moodle_url('/mod/assign/view.php', array('id' => $cm->id)); $this->assertEquals($moodleurl, $event->get_url()); $this->assertEventContextNotUsed($event); $this->assertNotEmpty($event->get_name()); // Test user with no capabilities. // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles. assign_capability('mod/assign:view', CAP_PROHIBIT, $studentrole->id, $context->id); accesslib_clear_all_caches_for_unit_testing(); course_modinfo::clear_instance_cache(); try { mod_assign_external::view_submission_status($assign->id); $this->fail('Exception expected due to missing capability.'); } catch (moodle_exception $e) { $this->assertEquals('requireloginerror', $e->errorcode); } }
/** * Returns the contexts the user can access. * * The returned value is a multidimensional array because some search engines can group * information and there will be a performance benefit on passing only some contexts * instead of the whole context array set. * * @return bool|array Indexed by area identifier (component + area name). Returns true if the user can see everything. */ protected function get_areas_user_accesses() { global $CFG, $USER; // All results for admins. Eventually we could add a new capability for managers. if (is_siteadmin()) { return true; } $areasbylevel = array(); // Split areas by context level so we only iterate only once through courses and cms. $searchareas = static::get_search_areas_list(true); foreach ($searchareas as $areaid => $unused) { $classname = static::get_area_classname($areaid); $searcharea = new $classname(); foreach ($classname::get_levels() as $level) { $areasbylevel[$level][$areaid] = $searcharea; } } // This will store area - allowed contexts relations. $areascontexts = array(); if (!empty($areasbylevel[CONTEXT_SYSTEM])) { // We add system context to all search areas working at this level. Here each area is fully responsible of // the access control as we can not automate much, we can not even check guest access as some areas might // want to allow guests to retrieve data from them. $systemcontextid = \context_system::instance()->id; foreach ($areasbylevel[CONTEXT_SYSTEM] as $areaid => $searchclass) { $areascontexts[$areaid][] = $systemcontextid; } } // Get the courses where the current user has access. $courses = enrol_get_my_courses(array('id', 'cacherev')); $courses[SITEID] = get_course(SITEID); $site = \course_modinfo::instance(SITEID); foreach ($courses as $course) { // Info about the course modules. $modinfo = get_fast_modinfo($course); if (!empty($areasbylevel[CONTEXT_COURSE])) { // Add the course contexts the user can view. $coursecontext = \context_course::instance($course->id); foreach ($areasbylevel[CONTEXT_COURSE] as $areaid => $searchclass) { if ($course->visible || has_capability('moodle/course:viewhiddencourses', $coursecontext)) { $areascontexts[$areaid][$coursecontext->id] = $coursecontext->id; } } } if (!empty($areasbylevel[CONTEXT_MODULE])) { // Add the module contexts the user can view (cm_info->uservisible). foreach ($areasbylevel[CONTEXT_MODULE] as $areaid => $searchclass) { // Removing the plugintype 'mod_' prefix. $modulename = substr($searchclass->get_component_name(), 4); $modinstances = $modinfo->get_instances_of($modulename); foreach ($modinstances as $modinstance) { if ($modinstance->uservisible) { $areascontexts[$areaid][$modinstance->context->id] = $modinstance->context->id; } } } } } return $areascontexts; }
/** * Retrieves the forced language of the course of null if not forced language. * @param course_modinfo $course_info : course_modinfo object of the course * @return string|NULL $lang: forced language of the course or null if not language is forced */ function block_intuitel_get_course_lang($course_info) { $course = $course_info->get_course(); if ($course->lang != null) { $lang = block_intuitel_get_parent_lang($course->lang); } else { $lang = null; } return $lang; }
/** * Works out whether activity is visible *for current user* - if this is false, they * aren't allowed to access it. * @return void */ private function update_user_visible() { global $CFG; $modcontext = get_context_instance(CONTEXT_MODULE, $this->id); $userid = $this->modinfo->get_user_id(); $this->uservisible = true; // Check visibility/availability conditions. if ((!$this->visible or !$this->available) and !has_capability('moodle/course:viewhiddenactivities', $modcontext, $userid)) { // If the activity is hidden or unavailable, and you don't have viewhiddenactivities, // set it so that user can't see or access it. $this->uservisible = false; } // Check group membership. The grouping option makes the activity // completely invisible as it does not apply to the user at all. if (!empty($CFG->enablegroupmembersonly) and !empty($this->groupmembersonly) and !has_capability('moodle/site:accessallgroups', $modcontext, $userid)) { // If the activity has 'group members only' and you don't have accessallgroups... $groups = $this->modinfo->get_groups($this->groupingid); if (empty($groups)) { // ...and you don't belong to a group, then set it so you can't see/access it $this->uservisible = false; // Ensure activity is completely hidden from user. $this->showavailability = 0; } } }
/** * Returns the LOId of the sections (not empty, with a summary or with cms) of a course * @param course_modinfo $rawData : course_modinfo object * @return \intuitel\LOId : array with LOID of children */ private function getChildren($rawData) { $sections = $rawData->get_section_info_all(); $hasChildren = array(); foreach ($sections as $section) { $sectionFactory = new SectionLOFactory(); $sectionLO = $sectionFactory->createLOFromNative($section); if ($sectionLO != null) { //empty section: no name or summary $sectionLOid = $sectionLO->getloId(); $hasChildren[] = $sectionLOid; } } return $hasChildren; }
/** * Test view_lti */ public function test_view_lti() { global $DB; // Test invalid instance id. try { mod_lti_external::view_lti(0); $this->fail('Exception expected due to invalid mod_lti instance id.'); } catch (moodle_exception $e) { $this->assertEquals('invalidrecord', $e->errorcode); } // Test not-enrolled user. $usernotenrolled = self::getDataGenerator()->create_user(); $this->setUser($usernotenrolled); try { mod_lti_external::view_lti($this->lti->id); $this->fail('Exception expected due to not enrolled user.'); } catch (moodle_exception $e) { $this->assertEquals('requireloginerror', $e->errorcode); } // Test user with full capabilities. $this->setUser($this->student); // Trigger and capture the event. $sink = $this->redirectEvents(); $result = mod_lti_external::view_lti($this->lti->id); $result = external_api::clean_returnvalue(mod_lti_external::view_lti_returns(), $result); $events = $sink->get_events(); $this->assertCount(1, $events); $event = array_shift($events); // Checking that the event contains the expected values. $this->assertInstanceOf('\\mod_lti\\event\\course_module_viewed', $event); $this->assertEquals($this->context, $event->get_context()); $moodlelti = new \moodle_url('/mod/lti/view.php', array('id' => $this->cm->id)); $this->assertEquals($moodlelti, $event->get_url()); $this->assertEventContextNotUsed($event); $this->assertNotEmpty($event->get_name()); // Test user with no capabilities. // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles. assign_capability('mod/lti:view', CAP_PROHIBIT, $this->studentrole->id, $this->context->id); // Empty all the caches that may be affected by this change. accesslib_clear_all_caches_for_unit_testing(); course_modinfo::clear_instance_cache(); try { mod_lti_external::view_lti($this->lti->id); $this->fail('Exception expected due to missing capability.'); } catch (moodle_exception $e) { $this->assertEquals('requireloginerror', $e->errorcode); } }
/** * Returns course ID - from course_sections table * * @return int */ private function get_course() { return $this->modinfo->get_course_id(); }
/** * Checks whether the module's conditional access settings mean that the user cannot see the activity at all * * @return bool True if the user cannot see the module. False if the activity is either available or should be greyed out. */ public function is_user_access_restricted_by_conditional_access() { global $CFG, $USER; if (empty($CFG->enableavailability)) { return false; } // If module will always be visible anyway (but greyed out), don't bother checking anything else if ($this->showavailability == CONDITION_STUDENTVIEW_SHOW) { return false; } // Can the user see hidden modules? $modcontext = context_module::instance($this->id); $userid = $this->modinfo->get_user_id(); if (has_capability('moodle/course:viewhiddenactivities', $modcontext, $userid)) { return false; } // Is the module hidden due to unmet conditions? if (!$this->available) { return true; } return false; }
/** * Test validate_attempt */ public function test_validate_attempt() { global $DB; // Create a new quiz with one attempt started. list($quiz, $context, $quizobj, $attempt, $attemptobj) = $this->create_quiz_with_questions(true); $this->setUser($this->student); // Invalid attempt. try { $params = array('attemptid' => -1, 'page' => 0); testable_mod_quiz_external::validate_attempt($params); $this->fail('Exception expected due to invalid attempt id.'); } catch (dml_missing_record_exception $e) { $this->assertEquals('invalidrecord', $e->errorcode); } // Test OK case. $params = array('attemptid' => $attempt->id, 'page' => 0); $result = testable_mod_quiz_external::validate_attempt($params); $this->assertEquals($attempt->id, $result[0]->get_attempt()->id); $this->assertEquals([], $result[1]); // Test with preflight data. $quiz->password = '******'; $DB->update_record('quiz', $quiz); try { $params = array('attemptid' => $attempt->id, 'page' => 0, 'preflightdata' => array(array("name" => "quizpassword", "value" => 'bad'))); testable_mod_quiz_external::validate_attempt($params); $this->fail('Exception expected due to invalid passwod.'); } catch (moodle_exception $e) { $this->assertEquals(get_string('passworderror', 'quizaccess_password'), $e->errorcode); } // Now, try everything correct. $params['preflightdata'][0]['value'] = 'abc'; $result = testable_mod_quiz_external::validate_attempt($params); $this->assertEquals($attempt->id, $result[0]->get_attempt()->id); $this->assertEquals([], $result[1]); // Page out of range. $DB->update_record('quiz', $quiz); $params['page'] = 4; try { testable_mod_quiz_external::validate_attempt($params); $this->fail('Exception expected due to page out of range.'); } catch (moodle_quiz_exception $e) { $this->assertEquals('Invalid page number', $e->errorcode); } $params['page'] = 0; // Try to open attempt in closed quiz. $quiz->timeopen = time() - WEEKSECS; $quiz->timeclose = time() - DAYSECS; $DB->update_record('quiz', $quiz); // This should work, ommit access rules. testable_mod_quiz_external::validate_attempt($params, false); // Get a generic error because prior to checking the dates the attempt is closed. try { testable_mod_quiz_external::validate_attempt($params); $this->fail('Exception expected due to passed dates.'); } catch (moodle_quiz_exception $e) { $this->assertEquals('attempterror', $e->errorcode); } // Finish the attempt. $attemptobj = quiz_attempt::create($attempt->id); $attemptobj->process_finish(time(), false); try { testable_mod_quiz_external::validate_attempt($params, false); $this->fail('Exception expected due to attempt finished.'); } catch (moodle_quiz_exception $e) { $this->assertEquals('attemptalreadyclosed', $e->errorcode); } // Test user with no capabilities. // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles. assign_capability('mod/quiz:attempt', CAP_PROHIBIT, $this->studentrole->id, $context->id); // Empty all the caches that may be affected by this change. accesslib_clear_all_caches_for_unit_testing(); course_modinfo::clear_instance_cache(); try { testable_mod_quiz_external::validate_attempt($params); $this->fail('Exception expected due to missing permissions.'); } catch (required_capability_exception $e) { $this->assertEquals('nopermissions', $e->errorcode); } // Now try with a different user. $this->setUser($this->teacher); $params['page'] = 0; try { testable_mod_quiz_external::validate_attempt($params); $this->fail('Exception expected due to not your attempt.'); } catch (moodle_quiz_exception $e) { $this->assertEquals('notyourattempt', $e->errorcode); } }
/** * Test get_calendar_events */ public function test_get_calendar_events() { global $DB, $USER; $this->resetAfterTest(true); $this->setAdminUser(); // Create a few stuff to test with. $user = $this->getDataGenerator()->create_user(); $course = $this->getDataGenerator()->create_course(); $record = new stdClass(); $record->courseid = $course->id; $group = $this->getDataGenerator()->create_group($record); $beforecount = $DB->count_records('event'); // Let's create a few events. $siteevent = $this->create_calendar_event('site', $USER->id, 'site'); // This event will have description with an inline fake image. $draftidfile = file_get_unused_draft_itemid(); $usercontext = context_course::instance($course->id); $filerecord = array('contextid' => $usercontext->id, 'component' => 'user', 'filearea' => 'draft', 'itemid' => $draftidfile, 'filepath' => '/', 'filename' => 'fakeimage.png'); $fs = get_file_storage(); $fs->create_file_from_string($filerecord, 'img contents'); $record = new stdClass(); $record->courseid = $course->id; $record->description = array('format' => FORMAT_HTML, 'text' => 'Text with img <img src="@@PLUGINFILE@@/fakeimage.png">', 'itemid' => $draftidfile); $courseevent = $this->create_calendar_event('course', $USER->id, 'course', 2, time(), $record); $userevent = $this->create_calendar_event('user', $USER->id); $record = new stdClass(); $record->courseid = $course->id; $record->groupid = $group->id; $groupevent = $this->create_calendar_event('group', $USER->id, 'group', 0, time(), $record); $paramevents = array('eventids' => array($siteevent->id), 'courseids' => array($course->id), 'groupids' => array($group->id)); $options = array('siteevents' => true, 'userevents' => true); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); // Check to see if we got all events. $this->assertEquals(5, count($events['events'])); $this->assertEquals(0, count($events['warnings'])); $options = array('siteevents' => true, 'userevents' => true, 'timeend' => time() + 7 * WEEKSECS); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(5, count($events['events'])); $this->assertEquals(0, count($events['warnings'])); // Expect the same URL in the description of two different events (because they are repeated). $coursecontext = context_course::instance($course->id); $expectedurl = "webservice/pluginfile.php/{$coursecontext->id}/calendar/event_description/{$courseevent->id}/fakeimage.png"; $withdescription = 0; foreach ($events['events'] as $event) { if (!empty($event['description'])) { $withdescription++; $this->assertContains($expectedurl, $event['description']); } } $this->assertEquals(2, $withdescription); // Let's play around with caps. $this->setUser($user); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(2, count($events['events'])); // site, user. $this->assertEquals(2, count($events['warnings'])); // course, group. $role = $DB->get_record('role', array('shortname' => 'student')); $this->getDataGenerator()->enrol_user($user->id, $course->id, $role->id); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(4, count($events['events'])); // site, user, both course events. $this->assertEquals(1, count($events['warnings'])); // group. $options = array('siteevents' => true, 'userevents' => true, 'timeend' => time() + HOURSECS); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(3, count($events['events'])); // site, user, one course event. $this->assertEquals(1, count($events['warnings'])); // group. groups_add_member($group, $user); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(4, count($events['events'])); // site, user, group, one course event. $this->assertEquals(0, count($events['warnings'])); $paramevents = array('courseids' => array($course->id), 'groupids' => array($group->id)); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(4, count($events['events'])); // site, user, group, one course event. $this->assertEquals(0, count($events['warnings'])); $paramevents = array('groupids' => array($group->id, 23)); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(3, count($events['events'])); // site, user, group. $this->assertEquals(1, count($events['warnings'])); $paramevents = array('courseids' => array(23)); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(2, count($events['events'])); // site, user. $this->assertEquals(1, count($events['warnings'])); $paramevents = array(); $options = array('siteevents' => false, 'userevents' => false, 'timeend' => time() + 7 * WEEKSECS); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(0, count($events['events'])); // nothing returned. $this->assertEquals(0, count($events['warnings'])); $paramevents = array('eventids' => array($siteevent->id, $groupevent->id)); $options = array('siteevents' => false, 'userevents' => false, 'timeend' => time() + 7 * WEEKSECS); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(2, count($events['events'])); // site, group. $this->assertEquals(0, count($events['warnings'])); $paramevents = array('eventids' => array($siteevent->id)); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(1, count($events['events'])); // site. $this->assertEquals(0, count($events['warnings'])); // Try getting a course event by its id. $paramevents = array('eventids' => array($courseevent->id)); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertEquals(1, count($events['events'])); $this->assertEquals(0, count($events['warnings'])); // Now, create an activity event. $this->setAdminUser(); $nexttime = time() + DAYSECS; $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id, 'duedate' => $nexttime)); $this->setUser($user); $paramevents = array('courseids' => array($course->id)); $options = array('siteevents' => true, 'userevents' => true, 'timeend' => time() + WEEKSECS); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); $this->assertCount(5, $events['events']); // Hide the assignment. set_coursemodule_visible($assign->cmid, 0); // Empty all the caches that may be affected by this change. accesslib_clear_all_caches_for_unit_testing(); course_modinfo::clear_instance_cache(); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); // Expect one less. $this->assertCount(4, $events['events']); }
private function fill_table_recursive(&$element) { global $DB, $CFG; $type = $element['type']; $depth = $element['depth']; $grade_object = $element['object']; $eid = $grade_object->id; $element['userid'] = $this->user->id; $fullname = $this->gtree->get_element_header($element, true, true, true); $data = array(); $hidden = ''; $excluded = ''; $class = ''; $classfeedback = ''; // If this is a hidden grade category, hide it completely from the user if ($type == 'category' && $grade_object->is_hidden() && !$this->canviewhidden && ($this->showhiddenitems == GRADE_REPORT_USER_HIDE_HIDDEN || $this->showhiddenitems == GRADE_REPORT_USER_HIDE_UNTIL && !$grade_object->is_hiddenuntil())) { return false; } if ($type == 'category') { $this->evenodd[$depth] = ($this->evenodd[$depth] + 1) % 2; } $alter = $this->evenodd[$depth] == 0 ? 'even' : 'odd'; /// Process those items that have scores associated if ($type == 'item' or $type == 'categoryitem' or $type == 'courseitem') { $header_row = "row_{$eid}_{$this->user->id}"; $header_cat = "cat_{$grade_object->categoryid}_{$this->user->id}"; if (!($grade_grade = grade_grade::fetch(array('itemid' => $grade_object->id, 'userid' => $this->user->id)))) { $grade_grade = new grade_grade(); $grade_grade->userid = $this->user->id; $grade_grade->itemid = $grade_object->id; } $grade_grade->load_grade_item(); /// Hidden Items if ($grade_grade->grade_item->is_hidden()) { $hidden = ' dimmed_text'; } $hide = false; // If this is a hidden grade item, hide it completely from the user. if ($grade_grade->is_hidden() && !$this->canviewhidden && ($this->showhiddenitems == GRADE_REPORT_USER_HIDE_HIDDEN || $this->showhiddenitems == GRADE_REPORT_USER_HIDE_UNTIL && !$grade_grade->is_hiddenuntil())) { $hide = true; } else { if (!empty($grade_object->itemmodule) && !empty($grade_object->iteminstance)) { // The grade object can be marked visible but still be hidden if // the student cannot see the activity due to conditional access // and it's set to be hidden entirely. $instances = $this->modinfo->get_instances_of($grade_object->itemmodule); if (!empty($instances[$grade_object->iteminstance])) { $cm = $instances[$grade_object->iteminstance]; if (!$cm->uservisible) { // If there is 'availableinfo' text then it is only greyed // out and not entirely hidden. if (!$cm->availableinfo) { $hide = true; } } } } } if (!$hide) { /// Excluded Item if ($grade_grade->is_excluded()) { $fullname .= ' [' . get_string('excluded', 'grades') . ']'; $excluded = ' excluded'; } /// Other class information $class = "{$hidden} {$excluded}"; if ($this->switch) { // alter style based on whether aggregation is first or last $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " " . $alter . "d{$depth} baggt b2b" : " item b1b"; } else { $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " " . $alter . "d{$depth} baggb" : " item b1b"; } if ($type == 'categoryitem' or $type == 'courseitem') { $header_cat = "cat_{$grade_object->iteminstance}_{$this->user->id}"; } /// Name $data['itemname']['content'] = $fullname; $data['itemname']['class'] = $class; $data['itemname']['colspan'] = $this->maxdepth - $depth; $data['itemname']['celltype'] = 'th'; $data['itemname']['id'] = $header_row; /// Actual Grade $gradeval = $grade_grade->finalgrade; if (!$this->canviewhidden) { /// Virtual Grade (may be calculated excluding hidden items etc). $adjustedgrade = $this->blank_hidden_total_and_adjust_bounds($this->courseid, $grade_grade->grade_item, $gradeval); $gradeval = $adjustedgrade['grade']; // We temporarily adjust the view of this grade item - because the min and // max are affected by the hidden values in the aggregation. $grade_grade->grade_item->grademax = $adjustedgrade['grademax']; $grade_grade->grade_item->grademin = $adjustedgrade['grademin']; } if ($this->showfeedback) { // Copy $class before appending itemcenter as feedback should not be centered $classfeedback = $class; } $class .= " itemcenter "; if ($this->showweight) { $data['weight']['class'] = $class; $data['weight']['content'] = '-'; $data['weight']['headers'] = "{$header_cat} {$header_row} weight"; // has a weight assigned, might be extra credit if ($grade_object->aggregationcoef > 0 && $type != 'courseitem') { $data['weight']['content'] = number_format($grade_object->aggregationcoef, 2); } } if ($this->showgrade) { if ($grade_grade->grade_item->needsupdate) { $data['grade']['class'] = $class . ' gradingerror'; $data['grade']['content'] = get_string('error'); } else { if (!empty($CFG->grade_hiddenasdate) and $grade_grade->get_datesubmitted() and !$this->canviewhidden and $grade_grade->is_hidden() and !$grade_grade->grade_item->is_category_item() and !$grade_grade->grade_item->is_course_item()) { // the problem here is that we do not have the time when grade value was modified, 'timemodified' is general modification date for grade_grades records $class .= ' datesubmitted'; $data['grade']['class'] = $class; $data['grade']['content'] = get_string('submittedon', 'grades', userdate($grade_grade->get_datesubmitted(), get_string('strftimedatetimeshort'))); } else { if ($grade_grade->is_hidden()) { $data['grade']['class'] = $class . ' dimmed_text'; $data['grade']['content'] = '-'; if ($this->canviewhidden) { $data['grade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true); } } else { $data['grade']['class'] = $class; $data['grade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true); } } } $data['grade']['headers'] = "{$header_cat} {$header_row} grade"; } // Range if ($this->showrange) { $data['range']['class'] = $class; $data['range']['content'] = $grade_grade->grade_item->get_formatted_range(GRADE_DISPLAY_TYPE_REAL, $this->rangedecimals); $data['range']['headers'] = "{$header_cat} {$header_row} range"; } // Percentage if ($this->showpercentage) { if ($grade_grade->grade_item->needsupdate) { $data['percentage']['class'] = $class . ' gradingerror'; $data['percentage']['content'] = get_string('error'); } else { if ($grade_grade->is_hidden()) { $data['percentage']['class'] = $class . ' dimmed_text'; $data['percentage']['content'] = '-'; if ($this->canviewhidden) { $data['percentage']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE); } } else { $data['percentage']['class'] = $class; $data['percentage']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE); } } $data['percentage']['headers'] = "{$header_cat} {$header_row} percentage"; } // Lettergrade if ($this->showlettergrade) { if ($grade_grade->grade_item->needsupdate) { $data['lettergrade']['class'] = $class . ' gradingerror'; $data['lettergrade']['content'] = get_string('error'); } else { if ($grade_grade->is_hidden()) { $data['lettergrade']['class'] = $class . ' dimmed_text'; if (!$this->canviewhidden) { $data['lettergrade']['content'] = '-'; } else { $data['lettergrade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_LETTER); } } else { $data['lettergrade']['class'] = $class; $data['lettergrade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_LETTER); } } $data['lettergrade']['headers'] = "{$header_cat} {$header_row} lettergrade"; } // Rank if ($this->showrank) { if ($grade_grade->grade_item->needsupdate) { $data['rank']['class'] = $class . ' gradingerror'; $data['rank']['content'] = get_string('error'); } elseif ($grade_grade->is_hidden()) { $data['rank']['class'] = $class . ' dimmed_text'; $data['rank']['content'] = '-'; } else { if (is_null($gradeval)) { // no grade, no rank $data['rank']['class'] = $class; $data['rank']['content'] = '-'; } else { /// find the number of users with a higher grade $sql = "SELECT COUNT(DISTINCT(userid))\n FROM {grade_grades}\n WHERE finalgrade > ?\n AND itemid = ?\n AND hidden = 0"; $rank = $DB->count_records_sql($sql, array($grade_grade->finalgrade, $grade_grade->grade_item->id)) + 1; $data['rank']['class'] = $class; $data['rank']['content'] = "{$rank}/" . $this->get_numusers(false); // total course users } } $data['rank']['headers'] = "{$header_cat} {$header_row} rank"; } // Average if ($this->showaverage) { $data['average']['class'] = $class; if (!empty($this->gtree->items[$eid]->avg)) { $data['average']['content'] = $this->gtree->items[$eid]->avg; } else { $data['average']['content'] = '-'; } $data['average']['headers'] = "{$header_cat} {$header_row} average"; } // Feedback if ($this->showfeedback) { if ($grade_grade->overridden > 0 and ($type == 'categoryitem' or $type == 'courseitem')) { $data['feedback']['class'] = $classfeedback . ' feedbacktext'; $data['feedback']['content'] = get_string('overridden', 'grades') . ': ' . format_text($grade_grade->feedback, $grade_grade->feedbackformat); } else { if (empty($grade_grade->feedback) or !$this->canviewhidden and $grade_grade->is_hidden()) { $data['feedback']['class'] = $classfeedback . ' feedbacktext'; $data['feedback']['content'] = ' '; } else { $data['feedback']['class'] = $classfeedback . ' feedbacktext'; $data['feedback']['content'] = format_text($grade_grade->feedback, $grade_grade->feedbackformat); } } $data['feedback']['headers'] = "{$header_cat} {$header_row} feedback"; } } } /// Category if ($type == 'category') { $data['leader']['class'] = $class . ' ' . $alter . "d{$depth} b1t b2b b1l"; $data['leader']['rowspan'] = $element['rowspan']; if ($this->switch) { // alter style based on whether aggregation is first or last $data['itemname']['class'] = $class . ' ' . $alter . "d{$depth} b1b b1t"; } else { $data['itemname']['class'] = $class . ' ' . $alter . "d{$depth} b2t"; } $data['itemname']['colspan'] = $this->maxdepth - $depth + count($this->tablecolumns) - 1; $data['itemname']['content'] = $fullname; $data['itemname']['celltype'] = 'th'; $data['itemname']['id'] = "cat_{$grade_object->id}_{$this->user->id}"; } /// Add this row to the overall system $this->tabledata[] = $data; /// Recursively iterate through all child elements if (isset($element['children'])) { foreach ($element['children'] as $key => $child) { $this->fill_table_recursive($element['children'][$key]); } } }
$coursecontext = context_course::instance($course->id); if (!has_capability('moodle/course:manageactivities', $coursecontext)) { echo get_string('progress_dating_error_access', 'block_snap'); //check the start dates for the course } elseif (!$course->startdate) { echo html_writer::tag('p', get_string('progress_dating_error', 'block_snap'), array('class' => 'alert alert-danger')); //check to see if completion is enabled for the course } elseif (!$course->enablecompletion) { echo html_writer::tag('p', get_string('no_completion_tracking', 'block_snap'), array('class' => 'alert alert-danger')); //otherwise, display the dating tool } else { echo html_writer::tag('p', get_string('progress_dating_description', 'block_snap'), array('class' => 'alert alert-info')); //create a warning about having javascript disabled that we will remove with javascript echo '<noscript>' . html_writer::tag('p', get_string('progress_dating_nojs', 'block_snap'), array('class' => 'snap-nojs alert alert-danger')) . '</noscript>'; //pull the modules course_modinfo::clear_instance_cache($course); $modinfo = get_fast_modinfo($course); $modlist = array(); $callist = array(); //establish the start date of the course $start = new DateTime(); $start->setTimestamp($course->startdate); //show when the course starts echo '<h3 class="course-starts">' . get_string('starts_on', 'block_snap') . ' <span>' . $start->format('l, F j, Y') . '</span></h3>' . PHP_EOL; //iterate all the modules and create each item for dragging around foreach ($modinfo->cms as $mod) { //if completion is not enabled, then skip it if (!$mod->completion) { continue; } //create the html for the module icon
/** * Preloads the list of active filters for all activities (modules) on the course * using two database queries. * * @param course_modinfo $modinfo Course object from get_fast_modinfo */ function filter_preload_activities(course_modinfo $modinfo) { global $DB, $FILTERLIB_PRIVATE; if (!isset($FILTERLIB_PRIVATE)) { $FILTERLIB_PRIVATE = new stdClass(); } // Don't repeat preload if (!isset($FILTERLIB_PRIVATE->preloaded)) { $FILTERLIB_PRIVATE->preloaded = array(); } if (!empty($FILTERLIB_PRIVATE->preloaded[$modinfo->get_course_id()])) { return; } $FILTERLIB_PRIVATE->preloaded[$modinfo->get_course_id()] = true; // Get contexts for all CMs $cmcontexts = array(); $cmcontextids = array(); foreach ($modinfo->get_cms() as $cm) { $modulecontext = context_module::instance($cm->id); $cmcontextids[] = $modulecontext->id; $cmcontexts[] = $modulecontext; } // Get course context and all other parents... $coursecontext = context_course::instance($modinfo->get_course_id()); $parentcontextids = explode('/', substr($coursecontext->path, 1)); $allcontextids = array_merge($cmcontextids, $parentcontextids); // Get all filter_active rows relating to all these contexts list($sql, $params) = $DB->get_in_or_equal($allcontextids); $filteractives = $DB->get_records_select('filter_active', "contextid {$sql}", $params); // Get all filter_config only for the cm contexts list($sql, $params) = $DB->get_in_or_equal($cmcontextids); $filterconfigs = $DB->get_records_select('filter_config', "contextid {$sql}", $params); // Note: I was a bit surprised that filter_config only works for the // most specific context (i.e. it does not need to be checked for course // context if we only care about CMs) however basede on code in // filter_get_active_in_context, this does seem to be correct. // Build course default active list. Initially this will be an array of // filter name => active score (where an active score >0 means it's active) $courseactive = array(); // Also build list of filter_active rows below course level, by contextid $remainingactives = array(); // Array lists filters that are banned at top level $banned = array(); // Add any active filters in parent contexts to the array foreach ($filteractives as $row) { $depth = array_search($row->contextid, $parentcontextids); if ($depth !== false) { // Find entry if (!array_key_exists($row->filter, $courseactive)) { $courseactive[$row->filter] = 0; } // This maths copes with reading rows in any order. Turning on/off // at site level counts 1, at next level down 4, at next level 9, // then 16, etc. This means the deepest level always wins, except // against the -9999 at top level. $courseactive[$row->filter] += ($depth + 1) * ($depth + 1) * $row->active; if ($row->active == TEXTFILTER_DISABLED) { $banned[$row->filter] = true; } } else { // Build list of other rows indexed by contextid if (!array_key_exists($row->contextid, $remainingactives)) { $remainingactives[$row->contextid] = array(); } $remainingactives[$row->contextid][] = $row; } } // Chuck away the ones that aren't active. foreach ($courseactive as $filter => $score) { if ($score <= 0) { unset($courseactive[$filter]); } else { $courseactive[$filter] = array(); } } // Loop through the contexts to reconstruct filter_active lists for each // cm on the course. if (!isset($FILTERLIB_PRIVATE->active)) { $FILTERLIB_PRIVATE->active = array(); } foreach ($cmcontextids as $contextid) { // Copy course list $FILTERLIB_PRIVATE->active[$contextid] = $courseactive; // Are there any changes to the active list? if (array_key_exists($contextid, $remainingactives)) { foreach ($remainingactives[$contextid] as $row) { if ($row->active > 0 && empty($banned[$row->filter])) { // If it's marked active for specific context, add entry // (doesn't matter if one exists already). $FILTERLIB_PRIVATE->active[$contextid][$row->filter] = array(); } else { // If it's marked inactive, remove entry (doesn't matter // if it doesn't exist). unset($FILTERLIB_PRIVATE->active[$contextid][$row->filter]); } } } } // Process all config rows to add config data to these entries. foreach ($filterconfigs as $row) { if (isset($FILTERLIB_PRIVATE->active[$row->contextid][$row->filter])) { $FILTERLIB_PRIVATE->active[$row->contextid][$row->filter][$row->name] = $row->value; } } }
/** * Test get_imscps_by_courses */ public function test_get_imscps_by_courses() { global $DB, $USER; $this->resetAfterTest(true); // As admin. $this->setAdminUser(); $course1 = self::getDataGenerator()->create_course(); $imscpoptions1 = array('course' => $course1->id, 'name' => 'First IMSCP'); $imscp1 = self::getDataGenerator()->create_module('imscp', $imscpoptions1); $course2 = self::getDataGenerator()->create_course(); $imscpoptions2 = array('course' => $course2->id, 'name' => 'Second IMSCP'); $imscp2 = self::getDataGenerator()->create_module('imscp', $imscpoptions2); $student1 = $this->getDataGenerator()->create_user(); $studentrole = $DB->get_record('role', array('shortname' => 'student')); // Enroll Student1 in Course1. self::getDataGenerator()->enrol_user($student1->id, $course1->id, $studentrole->id); $this->setUser($student1); $imscps = mod_imscp_external::get_imscps_by_courses(array()); $imscps = external_api::clean_returnvalue(mod_imscp_external::get_imscps_by_courses_returns(), $imscps); $this->assertCount(1, $imscps['imscps']); $this->assertEquals('First IMSCP', $imscps['imscps'][0]['name']); // As Student you cannot see some IMSCP properties like 'section'. $this->assertFalse(isset($imscps['imscps'][0]['section'])); // Student1 is not enrolled in this Course. // The webservice will give a warning! $imscps = mod_imscp_external::get_imscps_by_courses(array($course2->id)); $imscps = external_api::clean_returnvalue(mod_imscp_external::get_imscps_by_courses_returns(), $imscps); $this->assertCount(0, $imscps['imscps']); $this->assertEquals(1, $imscps['warnings'][0]['warningcode']); // Now as admin. $this->setAdminUser(); // As Admin we can see this IMSCP. $imscps = mod_imscp_external::get_imscps_by_courses(array($course2->id)); $imscps = external_api::clean_returnvalue(mod_imscp_external::get_imscps_by_courses_returns(), $imscps); $this->assertCount(1, $imscps['imscps']); $this->assertEquals('Second IMSCP', $imscps['imscps'][0]['name']); // As an Admin you can see some IMSCP properties like 'section'. $this->assertEquals(0, $imscps['imscps'][0]['section']); // Now, prohibit capabilities. $this->setUser($student1); $contextcourse1 = context_course::instance($course1->id); // Prohibit capability = mod:imscp:view on Course1 for students. assign_capability('mod/imscp:view', CAP_PROHIBIT, $studentrole->id, $contextcourse1->id); // Empty all the caches that may be affected by this change. accesslib_clear_all_caches_for_unit_testing(); course_modinfo::clear_instance_cache(); $imscps = mod_imscp_external::get_imscps_by_courses(array($course1->id)); $imscps = external_api::clean_returnvalue(mod_imscp_external::get_imscps_by_courses_returns(), $imscps); $this->assertCount(0, $imscps['imscps']); }