Merge branch 'MDL-29236-master' of git://github.com/sammarshallou/moodle
This commit is contained in:
+10
-4
@@ -1376,22 +1376,28 @@ function set_section_visible($courseid, $sectionnumber, $visibility) {
|
||||
function get_print_section_cm_text(cm_info $cm, $course) {
|
||||
global $OUTPUT;
|
||||
|
||||
// Get course context
|
||||
$coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
|
||||
|
||||
// Get content from modinfo if specified. Content displays either
|
||||
// in addition to the standard link (below), or replaces it if
|
||||
// the link is turned off by setting ->url to null.
|
||||
if (($content = $cm->get_content()) !== '') {
|
||||
// Improve filter performance by preloading filter setttings for all
|
||||
// activities on the course (this does nothing if called multiple
|
||||
// times)
|
||||
filter_preload_activities($cm->get_modinfo());
|
||||
|
||||
// Get module context
|
||||
$modulecontext = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
$labelformatoptions = new stdClass();
|
||||
$labelformatoptions->noclean = true;
|
||||
$labelformatoptions->overflowdiv = true;
|
||||
$labelformatoptions->context = $coursecontext;
|
||||
$labelformatoptions->context = $modulecontext;
|
||||
$content = format_text($content, FORMAT_HTML, $labelformatoptions);
|
||||
} else {
|
||||
$content = '';
|
||||
}
|
||||
|
||||
// Get course context
|
||||
$coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
|
||||
$stringoptions = new stdClass;
|
||||
$stringoptions->context = $coursecontext;
|
||||
$instancename = format_string($cm->name, true, $stringoptions);
|
||||
|
||||
+132
-1
@@ -816,7 +816,15 @@ function filter_get_all_local_settings($contextid) {
|
||||
* array('filter/tex' => array(), 'mod/glossary' => array('glossaryid', 123))
|
||||
*/
|
||||
function filter_get_active_in_context($context) {
|
||||
global $DB;
|
||||
global $DB, $FILTERLIB_PRIVATE;
|
||||
|
||||
// Use cache (this is a within-request cache only) if available. See
|
||||
// function filter_preload_activities.
|
||||
if (isset($FILTERLIB_PRIVATE->active) &&
|
||||
array_key_exists($context->id, $FILTERLIB_PRIVATE->active)) {
|
||||
return $FILTERLIB_PRIVATE->active[$context->id];
|
||||
}
|
||||
|
||||
$contextids = str_replace('/', ',', trim($context->path, '/'));
|
||||
|
||||
// The following SQL is tricky. It is explained on
|
||||
@@ -850,6 +858,129 @@ function filter_get_active_in_context($context) {
|
||||
return $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
// 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 = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
$cmcontextids[] = $modulecontext->id;
|
||||
$cmcontexts[] = $modulecontext;
|
||||
}
|
||||
|
||||
// Get course context and all other parents...
|
||||
$coursecontext = get_context_instance(CONTEXT_COURSE, $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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List all of the filters that are available in this context, and what the
|
||||
* local and inherited states of that filter are.
|
||||
|
||||
@@ -548,6 +548,180 @@ class filter_get_active_available_in_context_test extends UnitTestCaseUsingDatab
|
||||
}
|
||||
}
|
||||
|
||||
class filter_preload_activities_test extends UnitTestCaseUsingDatabase {
|
||||
private $syscontext, $catcontext, $coursecontext, $activity1context, $activity2context;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Make sure accesslib has cached a sensible system context object
|
||||
// before we switch to the test DB.
|
||||
$this->syscontext = get_context_instance(CONTEXT_SYSTEM);
|
||||
|
||||
// Create the table we need and switch to test DB.
|
||||
$this->create_test_tables(array('filter_active', 'filter_config', 'context',
|
||||
'course', 'course_modules', 'modules', 'course_sections',
|
||||
'course_modules_availability', 'grade_items'), 'lib');
|
||||
$this->create_test_tables(array('label'), 'mod/label');
|
||||
$this->switch_to_test_db();
|
||||
|
||||
// Set up systcontext in the test database.
|
||||
$this->syscontext->id = $this->testdb->insert_record('context', $this->syscontext);
|
||||
|
||||
// Make the course
|
||||
$course = (object)array(
|
||||
'shortname' => 'TEST101');
|
||||
$course->id = $this->testdb->insert_record('course', $course);
|
||||
|
||||
// Set up category and course contexts
|
||||
$this->catcontext = (object)array(
|
||||
'contextlevel' => CONTEXT_COURSECAT,
|
||||
'instanceid' => 1,
|
||||
'depth' => 2,
|
||||
'path' => '/1/2');
|
||||
$this->catcontext->id = $this->testdb->insert_record('context', $this->catcontext);
|
||||
$this->coursecontext = (object)array(
|
||||
'contextlevel' => CONTEXT_COURSE,
|
||||
'instanceid' => $course->id,
|
||||
'depth' => 3,
|
||||
'path' => '/1/2/3');
|
||||
$this->coursecontext->id = $this->testdb->insert_record('context', $this->coursecontext);
|
||||
|
||||
// Set up section
|
||||
$section = (object)array(
|
||||
'course' => $course->id);
|
||||
$section->id = $this->testdb->insert_record('course_sections', $section);
|
||||
|
||||
// Make course-modules
|
||||
$mod = (object)array(
|
||||
'name' => 'label',
|
||||
'visible' => 1);
|
||||
$mod->id = $this->testdb->insert_record('modules', $mod);
|
||||
$label1 = (object)array(
|
||||
'course' => $course->id,
|
||||
'intro' => 'Intro 1',
|
||||
'name' => 'Label 1');
|
||||
$label1->id = $this->testdb->insert_record('label', $label1);
|
||||
$cm1 = (object)array(
|
||||
'course' => $course->id,
|
||||
'section' => $section->id,
|
||||
'module' => $mod->id,
|
||||
'instance' => $label1->id);
|
||||
$cm1->id = $this->testdb->insert_record('course_modules', $cm1);
|
||||
$label2 = (object)array(
|
||||
'course' => $course->id,
|
||||
'intro' => 'Intro 2',
|
||||
'name' => 'Label 2');
|
||||
$label2->id = $this->testdb->insert_record('label', $label2);
|
||||
$cm2 = (object)array(
|
||||
'course' => $course->id,
|
||||
'section' => $section->id,
|
||||
'module' => $mod->id,
|
||||
'instance' => $label2->id);
|
||||
$cm2->id = $this->testdb->insert_record('course_modules', $cm2);
|
||||
$this->testdb->set_field('course_sections', 'sequence',
|
||||
"$cm1->id,$cm2->id", array('id' => $section->id));
|
||||
|
||||
// Set up activity contexts
|
||||
$this->activity1context = (object)array(
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
'instanceid' => $cm1->id,
|
||||
'depth' => 4,
|
||||
'path' => '/1/2/3/4');
|
||||
$this->activity1context->id =
|
||||
$this->testdb->insert_record('context', $this->activity1context);
|
||||
$this->activity2context = (object)array(
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
'instanceid' => $cm2->id,
|
||||
'depth' => 4,
|
||||
'path' => '/1/2/3/5');
|
||||
$this->activity2context->id =
|
||||
$this->testdb->insert_record('context', $this->activity2context);
|
||||
}
|
||||
|
||||
private function assert_matches($modinfo) {
|
||||
global $FILTERLIB_PRIVATE;
|
||||
|
||||
// Use preload cache...
|
||||
$FILTERLIB_PRIVATE = new stdClass;
|
||||
filter_preload_activities($modinfo);
|
||||
|
||||
// Get data and check no queries are made
|
||||
$before = $this->testdb->perf_get_reads();
|
||||
$plfilters1 = filter_get_active_in_context($this->activity1context);
|
||||
$plfilters2 = filter_get_active_in_context($this->activity2context);
|
||||
$after = $this->testdb->perf_get_reads();
|
||||
$this->assertEqual($before, $after);
|
||||
|
||||
// Repeat without cache and check it makes queries now
|
||||
$FILTERLIB_PRIVATE = new stdClass;
|
||||
$before = $this->testdb->perf_get_reads();
|
||||
$filters1 = filter_get_active_in_context($this->activity1context);
|
||||
$filters2 = filter_get_active_in_context($this->activity2context);
|
||||
$after = $this->testdb->perf_get_reads();
|
||||
$this->assertTrue($after > $before);
|
||||
|
||||
// Check they match
|
||||
$this->assertEqual($plfilters1, $filters1);
|
||||
$this->assertEqual($plfilters2, $filters2);
|
||||
}
|
||||
|
||||
public function test_preload() {
|
||||
global $FILTERLIB_PRIVATE;
|
||||
|
||||
// Get course and modinfo
|
||||
$course = $this->testdb->get_record('course', array('id'=>1));
|
||||
$modinfo = new course_modinfo($course, 1);
|
||||
|
||||
// Note: All the tests in this function check that the result from the
|
||||
// preloaded cache is the same as the result from calling the standard
|
||||
// function without preloading.
|
||||
|
||||
// Initially, check with no filters enabled
|
||||
$this->assert_matches($modinfo);
|
||||
|
||||
// Enable filter globally, check
|
||||
filter_set_global_state('filter/name', TEXTFILTER_ON);
|
||||
$this->assert_matches($modinfo);
|
||||
|
||||
// Disable for activity 2
|
||||
filter_set_local_state('filter/name', $this->activity2context->id, TEXTFILTER_OFF);
|
||||
$this->assert_matches($modinfo);
|
||||
|
||||
// Disable at category
|
||||
filter_set_local_state('filter/name', $this->catcontext->id, TEXTFILTER_OFF);
|
||||
$this->assert_matches($modinfo);
|
||||
|
||||
// Enable for activity 1
|
||||
filter_set_local_state('filter/name', $this->activity1context->id, TEXTFILTER_ON);
|
||||
$this->assert_matches($modinfo);
|
||||
|
||||
// Disable globally
|
||||
filter_set_global_state('filter/name', TEXTFILTER_DISABLED);
|
||||
$this->assert_matches($modinfo);
|
||||
|
||||
// Add another 2 filters
|
||||
filter_set_global_state('filter/frog', TEXTFILTER_ON);
|
||||
filter_set_global_state('filter/zombie', TEXTFILTER_ON);
|
||||
$this->assert_matches($modinfo);
|
||||
|
||||
// Disable random one of these in each context
|
||||
filter_set_local_state('filter/zombie', $this->activity1context->id, TEXTFILTER_OFF);
|
||||
filter_set_local_state('filter/frog', $this->activity2context->id, TEXTFILTER_OFF);
|
||||
$this->assert_matches($modinfo);
|
||||
|
||||
// Now do some filter options
|
||||
filter_set_local_config('filter/name', $this->activity1context->id, 'a', 'x');
|
||||
filter_set_local_config('filter/zombie', $this->activity1context->id, 'a', 'y');
|
||||
filter_set_local_config('filter/frog', $this->activity1context->id, 'a', 'z');
|
||||
// These last two don't do anything as they are not at final level but I
|
||||
// thought it would be good to have that verified in test
|
||||
filter_set_local_config('filter/frog', $this->coursecontext->id, 'q', 'x');
|
||||
filter_set_local_config('filter/frog', $this->catcontext->id, 'q', 'z');
|
||||
$this->assert_matches($modinfo);
|
||||
}
|
||||
}
|
||||
|
||||
class filter_delete_config_test extends UnitTestCaseUsingDatabase {
|
||||
protected $syscontext;
|
||||
|
||||
@@ -659,4 +833,3 @@ class filter_filter_set_applies_to_strings extends UnitTestCaseUsingDatabase {
|
||||
$this->assertTrue($CFG->filterall);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user