Merge branch 'MDL-29236-master' of git://github.com/sammarshallou/moodle

This commit is contained in:
Eloy Lafuente (stronk7)
2011-09-12 20:37:24 +02:00
3 changed files with 316 additions and 6 deletions
+10 -4
View File
@@ -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
View File
@@ -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.
+174 -1
View File
@@ -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);
}
}