From 1c9dbff82ce68bc483ad1f1533c49a9ce5edb4bd Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Fri, 3 Apr 2009 13:35:29 +0800 Subject: filters: MDL-7336 database tables for per-context filter settings. --- lib/db/install.xml | 38 +++++++++++++++++++++++++++++++-- lib/db/upgrade.php | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ version.php | 2 +- 3 files changed, 93 insertions(+), 4 deletions(-) diff --git a/lib/db/install.xml b/lib/db/install.xml index 4f65217..afe17d3 100644 --- a/lib/db/install.xml +++ b/lib/db/install.xml @@ -1,5 +1,5 @@ - @@ -270,7 +270,7 @@ - +
@@ -284,7 +284,39 @@
- +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index 4944439..09541c8 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -1524,6 +1524,7 @@ WHERE gradeitemid IS NOT NULL AND grademax IS NOT NULL"); /// Main savepoint reached upgrade_main_savepoint($result, 2009032000); } + if ($result && $oldversion < 2009032001) { /// Copy from role_allow_assign into the new table. $DB->execute('INSERT INTO {role_allow_switch} SELECT * FROM {role_allow_assign}'); @@ -1542,6 +1543,62 @@ WHERE gradeitemid IS NOT NULL AND grademax IS NOT NULL"); upgrade_main_savepoint($result, 2009033100); } + if ($result && $oldversion < 2009040300) { + + /// Define table filter_active to be created + $table = new xmldb_table('filter_active'); + + /// Adding fields to table filter_active + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null, null); + $table->add_field('filter', XMLDB_TYPE_CHAR, '32', null, XMLDB_NOTNULL, null, null, null, null); + $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, null); + $table->add_field('active', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, null, null, null); + $table->add_field('sortorder', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, null, '0'); + + /// Adding keys to table filter_active + $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); + $table->add_key('contextid', XMLDB_KEY_FOREIGN, array('contextid'), 'context', array('id')); + + /// Adding indexes to table filter_active + $table->add_index('contextid-filter', XMLDB_INDEX_UNIQUE, array('contextid', 'filter')); + + /// Conditionally launch create table for filter_active + if (!$dbman->table_exists($table)) { + $dbman->create_table($table); + } + + /// Main savepoint reached + upgrade_main_savepoint($result, 2009040300); + } + + if ($result && $oldversion < 2009040301) { + + /// Define table filter_config to be created + $table = new xmldb_table('filter_config'); + + /// Adding fields to table filter_config + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null, null); + $table->add_field('filter', XMLDB_TYPE_CHAR, '32', null, XMLDB_NOTNULL, null, null, null, null); + $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, null); + $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null, null, null); + $table->add_field('value', XMLDB_TYPE_TEXT, 'small', null, null, null, null, null, null); + + /// Adding keys to table filter_config + $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); + $table->add_key('contextid', XMLDB_KEY_FOREIGN, array('contextid'), 'context', array('id')); + + /// Adding indexes to table filter_config + $table->add_index('contextid-filter-name', XMLDB_INDEX_UNIQUE, array('contextid', 'filter', 'name')); + + /// Conditionally launch create table for filter_config + if (!$dbman->table_exists($table)) { + $dbman->create_table($table); + } + + /// Main savepoint reached + upgrade_main_savepoint($result, 2009040301); + } + return $result; } diff --git a/version.php b/version.php index 69f4df0..9274fe2 100644 --- a/version.php +++ b/version.php @@ -6,7 +6,7 @@ // This is compared against the values stored in the database to determine // whether upgrades should be performed (see lib/db/*.php) - $version = 2009040100; // YYYYMMDD = date of the last version bump + $version = 2009040301; // YYYYMMDD = date of the last version bump // XX = daily increments $release = '2.0 dev (Build: 20090403)'; // Human-friendly version name -- 1.5.6.3 From 1019c8495038aa471d5782ed8da0231244ca6f07 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Fri, 3 Apr 2009 16:35:54 +0800 Subject: filters: MDL-7336 upgrade $CFG->textfilters into filter_active table. --- lib/db/upgrade.php | 25 ++++++++++++++ lib/filterlib.php | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++ version.php | 2 +- 3 files changed, 116 insertions(+), 1 deletions(-) diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index 09541c8..2d39b13 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -1599,6 +1599,31 @@ WHERE gradeitemid IS NOT NULL AND grademax IS NOT NULL"); upgrade_main_savepoint($result, 2009040301); } + if ($result && $oldversion < 2009040302) { + /// Transfer current settings from $CFG->textfilters + $disabledfilters = filter_get_all_installed(); + if (empty($CFG->textfilters)) { + $activefilters = array(); + } else { + $activefilters = explode(',', $CFG->textfilters); + } + $syscontext = get_context_instance(CONTEXT_SYSTEM); + $sortorder = 1; + foreach ($activefilters as $filter) { + filter_set_global_state($filter, TEXTFILTER_ON, $sortorder); + $sortorder += 1; + unset($disabledfilters[$filter]); + } + foreach ($disabledfilters as $filter => $notused) { + filter_set_global_state($filter, TEXTFILTER_DISABLED, $sortorder); + $sortorder += 1; + } + unset_config('textfilters'); + + /// Main savepoint reached + upgrade_main_savepoint($result, 2009040302); + } + return $result; } diff --git a/lib/filterlib.php b/lib/filterlib.php index 74f203f..207da17 100644 --- a/lib/filterlib.php +++ b/lib/filterlib.php @@ -1,6 +1,13 @@ $DB->get_field('filter_active', 'MAX(sortorder)', array()) + 1) { + throw new coding_exception("Invalid sort order passed to filter_set_global_state."); + } + } + + // See if there is an existing record. + $syscontext = get_context_instance(CONTEXT_SYSTEM); + $rec = $DB->get_record('filter_active', array('filter' => $filter, 'contextid' => $syscontext->id)); + if (empty($rec)) { + $insert = true; + $rec = new stdClass; + $rec->filter = $filter; + $rec->contextid = $syscontext->id; + } else { + $insert = false; + if ($sortorder === false && !($rec->active == TEXTFILTER_DISABLED xor $state == TEXTFILTER_DISABLED)) { + $sortorder = $rec->sortorder; + } + } + + // Automatic sort order. + if ($sortorder === false) { + if ($state == TEXTFILTER_DISABLED) { + $prevmaxsortorder = $DB->get_field('filter_active', 'MAX(sortorder)', array()); + } else { + $prevmaxsortorder = $DB->get_field_select('filter_active', 'MAX(sortorder)', 'active <> ?', array(TEXTFILTER_DISABLED)); + } + if (empty($prevmaxsortorder)) { + $sortorder = 1; + } else { + $sortorder = $prevmaxsortorder + 1; + if (!$insert && $state == TEXTFILTER_DISABLED) { + $sortorder = $prevmaxsortorder; + } + } + } + + // Move any existing records out of the way of the sortorder. + if ($insert) { + $DB->execute('UPDATE {filter_active} SET sortorder = sortorder + 1 WHERE sortorder >= ?', array($sortorder)); + } else if ($sortorder != $rec->sortorder) { + $sparesortorder = $DB->get_field('filter_active', 'MIN(sortorder)', array()) - 1; + $DB->set_field('filter_active', 'sortorder', $sparesortorder, array('filter' => $filter, 'contextid' => $syscontext->id)); + if ($sortorder < $rec->sortorder) { + $DB->execute('UPDATE {filter_active} SET sortorder = sortorder + 1 WHERE sortorder >= ? AND sortorder < ?', + array($sortorder, $rec->sortorder)); + } else if ($sortorder > $rec->sortorder) { + $DB->execute('UPDATE {filter_active} SET sortorder = sortorder - 1 WHERE sortorder <= ? AND sortorder > ?', + array($sortorder, $rec->sortorder)); + } + } + + // Insert/update the new record. + $rec->active = $state; + $rec->sortorder = $sortorder; + if ($insert) { + $DB->insert_record('filter_active', $rec); + } else { + $DB->update_record('filter_active', $rec); + } +} + +/** * Process phrases intelligently found within a HTML text (such as adding links) * * param text the text that we are filtering diff --git a/version.php b/version.php index 9274fe2..8f0e3d0 100644 --- a/version.php +++ b/version.php @@ -6,7 +6,7 @@ // This is compared against the values stored in the database to determine // whether upgrades should be performed (see lib/db/*.php) - $version = 2009040301; // YYYYMMDD = date of the last version bump + $version = 2009040302; // YYYYMMDD = date of the last version bump // XX = daily increments $release = '2.0 dev (Build: 20090403)'; // Human-friendly version name -- 1.5.6.3 From 1d18cd44f4500a704c18c68999f6560681af4e90 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Fri, 3 Apr 2009 17:34:56 +0800 Subject: filters: MDL-7336 functions for getting and setting filter_config --- lib/filterlib.php | 48 ++++++- lib/simpletest/testfilterconfig.php | 268 +++++++++++++++++++++++++++++++++++ 2 files changed, 313 insertions(+), 3 deletions(-) create mode 100644 lib/simpletest/testfilterconfig.php diff --git a/lib/filterlib.php b/lib/filterlib.php index 207da17..368908f 100644 --- a/lib/filterlib.php +++ b/lib/filterlib.php @@ -161,9 +161,9 @@ function filter_get_all_installed() { /** * Set the global activated state for a text filter. - * @param $filter The filter name, for example 'filter/tex' or 'mod/glossary'. - * @param $state One of the values TEXTFILTER_ON, TEXTFILTER_OFF or TEXTFILTER_DISABLED. - * @param $sortorder (optional) a position in the sortorder to place this filter. + * @param string$filter The filter name, for example 'filter/tex' or 'mod/glossary'. + * @param integer $state One of the values TEXTFILTER_ON, TEXTFILTER_OFF or TEXTFILTER_DISABLED. + * @param integer $sortorder (optional) a position in the sortorder to place this filter. * If not given defaults to: * No change in order if we are updating an exsiting record, and not changing to or from TEXTFILTER_DISABLED. * Just after the last currently active filter for a change to TEXTFILTER_ON or TEXTFILTER_OFF @@ -243,6 +243,48 @@ function filter_set_global_state($filter, $state, $sortorder = false) { } /** + * Set a particular local config variable for a filter in a context. + * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'. + * @param integer $contextid The ID of the context to get the local config for. + * @param string $name the setting name. + * @param string $value the corresponding value. + */ +function filter_set_local_config($filter, $contextid, $name, $value) { + global $DB; + $rec = $DB->get_record('filter_config', array('filter' => $filter, 'contextid' => $contextid, 'name' => $name)); + $insert = false; + if (empty($rec)) { + $insert = true; + $rec = new stdClass; + $rec->filter = $filter; + $rec->contextid = $contextid; + $rec->name = $name; + } + + $rec->value = $value; + + if ($insert) { + $DB->insert_record('filter_config', $rec); + } else { + $DB->update_record('filter_config', $rec); + } +} + +/** + * Get local config variables for a filter in a context. Normally (when your + * filter is running) you don't need to call this, becuase the config is fetched + * for you automatically. You only need this, for example, when you are getting + * the config so you can show the user an editing from. + * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'. + * @param integer $contextid The ID of the context to get the local config for. + * @return array of name => value pairs. + */ +function filter_get_local_config($filter, $contextid) { + global $DB; + return $DB->get_records_menu('filter_config', array('filter' => $filter, 'contextid' => $contextid), '', 'name,value'); +} + +/** * Process phrases intelligently found within a HTML text (such as adding links) * * param text the text that we are filtering diff --git a/lib/simpletest/testfilterconfig.php b/lib/simpletest/testfilterconfig.php new file mode 100644 index 0000000..78e9cd7 --- /dev/null +++ b/lib/simpletest/testfilterconfig.php @@ -0,0 +1,268 @@ +libdir . '/filterlib.php'); + +/** + * Test functions that use just the filter_active table. + */ +class filter_active_test extends UnitTestCaseUsingDatabase { + private $syscontextid; + + public function setUp() { + // Make sure accesslib has cached a sensible system context object + // before we switch to the test DB. + $this->syscontextid = get_context_instance(CONTEXT_SYSTEM)->id; + + // Create the table we need and switch to test DB. + $this->create_test_table('filter_active', 'lib'); + $this->switch_to_test_db(); + } + + private function assert_only_one_filter_globally($filter, $state) { + $recs = $this->testdb->get_records('filter_active'); + $this->assertEqual(1, count($recs), 'More than one record returned %s.'); + $rec = reset($recs); + $this->assertEqual($rec->filter, $filter); + $this->assertEqual($rec->contextid, $this->syscontextid); + $this->assertEqual($rec->active, $state); + $this->assertEqual($rec->sortorder, 1); + } + + private function assert_global_sort_order($filters) { + $sortedfilters = $this->testdb->get_records_menu('filter_active', + array('contextid' => $this->syscontextid), 'sortorder', 'sortorder,filter'); + $testarray = array(); + $index = 1; + foreach($filters as $filter) { + $testarray[$index++] = $filter; + } + $this->assertEqual($testarray, $sortedfilters); + } + + public function test_set_filter_globally_on() { + // Setup fixture. + // Exercise SUT. + filter_set_global_state('filter/name', TEXTFILTER_ON, 1); + // Validate. + $this->assert_only_one_filter_globally('filter/name', TEXTFILTER_ON); + } + + public function test_set_filter_globally_off() { + // Setup fixture. + // Exercise SUT. + filter_set_global_state('filter/name', TEXTFILTER_OFF, 1); + // Validate. + $this->assert_only_one_filter_globally('filter/name', TEXTFILTER_OFF); + } + + public function test_set_filter_globally_disabled() { + // Setup fixture. + // Exercise SUT. + filter_set_global_state('filter/name', TEXTFILTER_DISABLED, 1); + // Validate. + $this->assert_only_one_filter_globally('filter/name', TEXTFILTER_DISABLED); + } + + public function test_global_config_exception_on_invalid_state() { + // Setup fixture. + // Set expectation. + $this->expectException(); + // Exercise SUT. + filter_set_global_state('filter/name', 0, 1); + } + + public function test_set_no_sortorder_clash() { + // Setup fixture. + // Exercise SUT. + filter_set_global_state('filter/one', TEXTFILTER_DISABLED, 1); + filter_set_global_state('filter/two', TEXTFILTER_DISABLED, 1); + // Validate - should have pushed other filters down. + $this->assert_global_sort_order(array('filter/two', 'filter/one')); + } + + public function test_auto_sort_order_disabled() { + // Setup fixture. + // Exercise SUT. + filter_set_global_state('filter/one', TEXTFILTER_DISABLED); + filter_set_global_state('filter/two', TEXTFILTER_DISABLED); + // Validate. + $this->assert_global_sort_order(array('filter/one', 'filter/two')); + } + + public function test_auto_sort_order_enabled() { + // Setup fixture. + // Exercise SUT. + filter_set_global_state('filter/one', TEXTFILTER_ON); + filter_set_global_state('filter/two', TEXTFILTER_OFF); + // Validate. + $this->assert_global_sort_order(array('filter/one', 'filter/two')); + } + + public function test_auto_sort_order_mixed() { + // Setup fixture. + // Exercise SUT. + filter_set_global_state('filter/0', TEXTFILTER_DISABLED); + filter_set_global_state('filter/1', TEXTFILTER_ON); + filter_set_global_state('filter/2', TEXTFILTER_DISABLED); + filter_set_global_state('filter/3', TEXTFILTER_OFF); + // Validate. + $this->assert_global_sort_order(array('filter/1', 'filter/3', 'filter/0', 'filter/2')); + } + + public function test_update_existing_dont_duplicate() { + // Setup fixture. + // Exercise SUT. + filter_set_global_state('filter/name', TEXTFILTER_ON); + filter_set_global_state('filter/name', TEXTFILTER_OFF); + // Validate. + $this->assert_only_one_filter_globally('filter/name', TEXTFILTER_OFF); + } + + public function test_sort_order_not_too_low() { + // Setup fixture. + filter_set_global_state('filter/1', TEXTFILTER_ON); + // Set expectation. + $this->expectException(); + // Exercise SUT. + filter_set_global_state('filter/2', TEXTFILTER_ON, 0); + } + + public function test_sort_order_not_too_high() { + // Setup fixture. + filter_set_global_state('filter/1', TEXTFILTER_ON); + // Set expectation. + $this->expectException(); + // Exercise SUT. + filter_set_global_state('filter/2', TEXTFILTER_ON, 3); + } + + public function test_update_reorder_down() { + // Setup fixture. + filter_set_global_state('filter/1', TEXTFILTER_ON); + filter_set_global_state('filter/2', TEXTFILTER_ON); + filter_set_global_state('filter/3', TEXTFILTER_ON); + // Exercise SUT. + filter_set_global_state('filter/2', TEXTFILTER_ON, 1); + // Validate. + $this->assert_global_sort_order(array('filter/2', 'filter/1', 'filter/3')); + } + + public function test_update_reorder_up() { + // Setup fixture. + filter_set_global_state('filter/1', TEXTFILTER_ON); + filter_set_global_state('filter/2', TEXTFILTER_ON); + filter_set_global_state('filter/3', TEXTFILTER_ON); + filter_set_global_state('filter/4', TEXTFILTER_ON); + // Exercise SUT. + filter_set_global_state('filter/2', TEXTFILTER_ON, 3); + // Validate. + $this->assert_global_sort_order(array('filter/1', 'filter/3', 'filter/2', 'filter/4')); + } + + public function test_auto_sort_order_change_to_enabled() { + // Setup fixture. + filter_set_global_state('filter/1', TEXTFILTER_ON); + filter_set_global_state('filter/2', TEXTFILTER_DISABLED); + filter_set_global_state('filter/3', TEXTFILTER_DISABLED); + // Exercise SUT. + filter_set_global_state('filter/3', TEXTFILTER_ON); + // Validate. + $this->assert_global_sort_order(array('filter/1', 'filter/3', 'filter/2')); + } + + public function test_auto_sort_order_change_to_disabled() { + // Setup fixture. + filter_set_global_state('filter/1', TEXTFILTER_ON); + filter_set_global_state('filter/2', TEXTFILTER_ON); + filter_set_global_state('filter/3', TEXTFILTER_DISABLED); + // Exercise SUT. + filter_set_global_state('filter/1', TEXTFILTER_DISABLED); + // Validate. + $this->assert_global_sort_order(array('filter/2', 'filter/3', 'filter/1')); + } +} + +/** + * Test functions that use just the filter_config table. + */ +class filter_config_test extends UnitTestCaseUsingDatabase { + public function setUp() { + // Create the table we need and switch to test DB. + $this->create_test_table('filter_config', 'lib'); + $this->switch_to_test_db(); + } + + private function assert_only_one_config($filter, $context, $name, $value) { + $recs = $this->testdb->get_records('filter_config'); + $this->assertEqual(1, count($recs), 'More than one record returned %s.'); + $rec = reset($recs); + $this->assertEqual($rec->filter, $filter); + $this->assertEqual($rec->contextid, $context); + $this->assertEqual($rec->name, $name); + $this->assertEqual($rec->value, $value); + } + + public function test_set_new_config() { + // Exercise SUT. + filter_set_local_config('filter/name', 123, 'settingname', 'An arbitrary value'); + // Validate. + $this->assert_only_one_config('filter/name', 123, 'settingname', 'An arbitrary value'); + } + + public function test_update_existing_config() { + // Setup fixture. + filter_set_local_config('filter/name', 123, 'settingname', 'An arbitrary value'); + // Exercise SUT. + filter_set_local_config('filter/name', 123, 'settingname', 'A changed value'); + // Validate. + $this->assert_only_one_config('filter/name', 123, 'settingname', 'A changed value'); + } + + public function test_filter_get_local_config() { + // Setup fixture. + filter_set_local_config('filter/name', 123, 'setting1', 'An arbitrary value'); + filter_set_local_config('filter/name', 123, 'setting2', 'Another arbitrary value'); + filter_set_local_config('filter/name', 122, 'settingname', 'Value from another context'); + filter_set_local_config('filter/other', 123, 'settingname', 'Someone else\'s value'); + // Exercise SUT. + $config = filter_get_local_config('filter/name', 123); + // Validate. + $this->assertEqual(array('setting1' => 'An arbitrary value', 'setting2' => 'Another arbitrary value'), $config); + } +} +?> -- 1.5.6.3 From f694e894e82bdb9a9300bdf5c454ff922396b961 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Fri, 3 Apr 2009 17:38:00 +0800 Subject: filters: MDL-7336 rework unit tests to use CheckSpecifiedFieldsExpectation. --- lib/simpletest/testfilterconfig.php | 20 ++++++++++++-------- 1 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/simpletest/testfilterconfig.php b/lib/simpletest/testfilterconfig.php index 78e9cd7..b3c4fc8 100644 --- a/lib/simpletest/testfilterconfig.php +++ b/lib/simpletest/testfilterconfig.php @@ -57,10 +57,12 @@ class filter_active_test extends UnitTestCaseUsingDatabase { $recs = $this->testdb->get_records('filter_active'); $this->assertEqual(1, count($recs), 'More than one record returned %s.'); $rec = reset($recs); - $this->assertEqual($rec->filter, $filter); - $this->assertEqual($rec->contextid, $this->syscontextid); - $this->assertEqual($rec->active, $state); - $this->assertEqual($rec->sortorder, 1); + $expectedrec = new stdClass; + $expectedrec->filter = $filter; + $expectedrec->contextid = $this->syscontextid; + $expectedrec->active = $state; + $expectedrec->sortorder = 1; + $this->assert(new CheckSpecifiedFieldsExpectation($expectedrec), $rec); } private function assert_global_sort_order($filters) { @@ -231,10 +233,12 @@ class filter_config_test extends UnitTestCaseUsingDatabase { $recs = $this->testdb->get_records('filter_config'); $this->assertEqual(1, count($recs), 'More than one record returned %s.'); $rec = reset($recs); - $this->assertEqual($rec->filter, $filter); - $this->assertEqual($rec->contextid, $context); - $this->assertEqual($rec->name, $name); - $this->assertEqual($rec->value, $value); + $expectedrec = new stdClass; + $expectedrec->filter = $filter; + $expectedrec->contextid = $context; + $expectedrec->name = $name; + $expectedrec->value = $value; + $this->assert(new CheckSpecifiedFieldsExpectation($expectedrec), $rec); } public function test_set_new_config() { -- 1.5.6.3 From 127611ec72cfbb89902f2e00f334157d3e89a97d Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Fri, 3 Apr 2009 18:03:38 +0800 Subject: filters: MDL-7336 functions for g/setting local filter_active overrides --- lib/filterlib.php | 52 ++++++++++++++++++++++-- lib/simpletest/testfilterconfig.php | 76 ++++++++++++++++++++++++++++++++++- 2 files changed, 122 insertions(+), 6 deletions(-) diff --git a/lib/filterlib.php b/lib/filterlib.php index 368908f..3e1fc91 100644 --- a/lib/filterlib.php +++ b/lib/filterlib.php @@ -5,6 +5,7 @@ * The states a filter can be in, stored in the filter_active table. */ define('TEXTFILTER_ON', 1); +define('TEXTFILTER_INHERIT', 0); define('TEXTFILTER_OFF', -1); define('TEXTFILTER_DISABLED', -9999); @@ -161,7 +162,7 @@ function filter_get_all_installed() { /** * Set the global activated state for a text filter. - * @param string$filter The filter name, for example 'filter/tex' or 'mod/glossary'. + * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'. * @param integer $state One of the values TEXTFILTER_ON, TEXTFILTER_OFF or TEXTFILTER_DISABLED. * @param integer $sortorder (optional) a position in the sortorder to place this filter. * If not given defaults to: @@ -174,8 +175,8 @@ function filter_set_global_state($filter, $state, $sortorder = false) { // Check requested state is valid. if (!in_array($state, array(TEXTFILTER_ON, TEXTFILTER_OFF, TEXTFILTER_DISABLED))) { - throw new coding_exception("Illegal option '$state' passed to filter_set_global_state. ' . - 'Must be one of TEXTFILTER_ON, TEXTFILTER_OFF or TEXTFILTER_DISABLED."); + throw new coding_exception("Illegal option '$state' passed to filter_set_global_state. " . + "Must be one of TEXTFILTER_ON, TEXTFILTER_OFF or TEXTFILTER_DISABLED."); } // Check sortorder is valid. @@ -243,9 +244,52 @@ function filter_set_global_state($filter, $state, $sortorder = false) { } /** + * Set the local activated state for a text filter. + * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'. + * @param integer $contextid The id of the context to get the local config for. + * @param integer $state One of the values TEXTFILTER_ON, TEXTFILTER_OFF or TEXTFILTER_INHERIT. + */ +function filter_set_local_state($filter, $contextid, $state) { + global $DB; + + // Check requested state is valid. + if (!in_array($state, array(TEXTFILTER_ON, TEXTFILTER_OFF, TEXTFILTER_INHERIT))) { + throw new coding_exception("Illegal option '$state' passed to filter_set_local_state. " . + "Must be one of TEXTFILTER_ON, TEXTFILTER_OFF or TEXTFILTER_INHERIT."); + } + + if ($contextid == get_context_instance(CONTEXT_SYSTEM)->id) { + throw new coding_exception('You cannot use filter_set_local_state ' . + 'with $contextid equal to the system context id.'); + } + + if ($state == TEXTFILTER_INHERIT) { + $DB->delete_records('filter_active', array('filter' => $filter, 'contextid' => $contextid)); + return; + } + + $rec = $DB->get_record('filter_active', array('filter' => $filter, 'contextid' => $contextid)); + $insert = false; + if (empty($rec)) { + $insert = true; + $rec = new stdClass; + $rec->filter = $filter; + $rec->contextid = $contextid; + } + + $rec->active = $state; + + if ($insert) { + $DB->insert_record('filter_active', $rec); + } else { + $DB->update_record('filter_active', $rec); + } +} + +/** * Set a particular local config variable for a filter in a context. * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'. - * @param integer $contextid The ID of the context to get the local config for. + * @param integer $contextid The id of the context to get the local config for. * @param string $name the setting name. * @param string $value the corresponding value. */ diff --git a/lib/simpletest/testfilterconfig.php b/lib/simpletest/testfilterconfig.php index b3c4fc8..5603c31 100644 --- a/lib/simpletest/testfilterconfig.php +++ b/lib/simpletest/testfilterconfig.php @@ -38,9 +38,9 @@ if (!defined('MOODLE_INTERNAL')) { require_once($CFG->libdir . '/filterlib.php'); /** - * Test functions that use just the filter_active table. + * Test functions that affect filter_active table with contextid = $syscontextid. */ -class filter_active_test extends UnitTestCaseUsingDatabase { +class filter_active_global_test extends UnitTestCaseUsingDatabase { private $syscontextid; public function setUp() { @@ -220,6 +220,78 @@ class filter_active_test extends UnitTestCaseUsingDatabase { } /** + * Test functions that affect filter_active table with contextid = $syscontextid. + */ +class filter_active_local_test extends UnitTestCaseUsingDatabase { + public function setUp() { + // Create the table we need and switch to test DB. + $this->create_test_table('filter_active', 'lib'); + $this->switch_to_test_db(); + } + + private function assert_only_one_local_setting($filter, $contextid, $state) { + $recs = $this->testdb->get_records('filter_active'); + $this->assertEqual(1, count($recs), 'More than one record returned %s.'); + $rec = reset($recs); + $expectedrec = new stdClass; + $expectedrec->filter = $filter; + $expectedrec->contextid = $contextid; + $expectedrec->active = $state; + $this->assert(new CheckSpecifiedFieldsExpectation($expectedrec), $rec); + } + + private function assert_no_local_setting() { + $this->assertEqual(0, $this->testdb->count_records('filter_active')); + } + + public function test_local_on() { + // Exercise SUT. + filter_set_local_state('filter/name', 123, TEXTFILTER_ON); + // Validate. + $this->assert_only_one_local_setting('filter/name', 123, TEXTFILTER_ON); + } + + public function test_local_off() { + // Exercise SUT. + filter_set_local_state('filter/name', 123, TEXTFILTER_OFF); + // Validate. + $this->assert_only_one_local_setting('filter/name', 123, TEXTFILTER_OFF); + } + + public function test_local_inherit() { + global $DB; + // Exercise SUT. + filter_set_local_state('filter/name', 123, TEXTFILTER_INHERIT); + // Validate. + $this->assert_no_local_setting(); + } + + public function test_local_invalid_state_throws_exception() { + // Set expectation. + $this->expectException(); + // Exercise SUT. + filter_set_local_state('filter/name', 123, -9999); + } + + public function test_throws_exception_when_setting_global() { + // Set expectation. + $this->expectException(); + // Exercise SUT. + filter_set_local_state('filter/name', get_context_instance(CONTEXT_SYSTEM)->id, TEXTFILTER_INHERIT); + } + + public function test_local_inherit_deletes_existing() { + global $DB; + // Setup fixture. + filter_set_local_state('filter/name', 123, TEXTFILTER_INHERIT); + // Exercise SUT. + filter_set_local_state('filter/name', 123, TEXTFILTER_INHERIT); + // Validate. + $this->assert_no_local_setting(); + } +} + +/** * Test functions that use just the filter_config table. */ class filter_config_test extends UnitTestCaseUsingDatabase { -- 1.5.6.3 From f2f93efef2c322cdf7f5963f2a4d4e39b8028057 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Fri, 3 Apr 2009 18:55:29 +0800 Subject: filters: MDL-7336 implement get_active_filters --- lib/filterlib.php | 47 ++++++++++++ lib/simpletest/testfilterconfig.php | 136 +++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+), 0 deletions(-) diff --git a/lib/filterlib.php b/lib/filterlib.php index 3e1fc91..19aef38 100644 --- a/lib/filterlib.php +++ b/lib/filterlib.php @@ -329,6 +329,53 @@ function filter_get_local_config($filter, $contextid) { } /** + * Get the list of active filters, in the order that they should be used + * for a particular context, along with any local configuration variables. + * + * @param object $context a context + * + * @return array an array where the keys are the filter names, for example + * 'filter/tex' or 'mod/glossary' and the values are any local + * configuration for that filter, as an array of name => value pairs + * from the filter_config table. In a lot of cases, this will be an + * empty array. So, an example return value for this function might be + * array('filter/tex' => array(), 'mod/glossary' => array('glossaryid', 123)) + */ +function get_active_filters($context) { + global $DB; + $contextids = str_replace('/', ',', trim($context->path, '/')); + + // The following SQL is tricky. It is explained on + // http://docs.moodle.org/en/Development:Filter_enable/disable_by_context + $rs = $DB->get_recordset_sql( + "SELECT active.filter, fc.name, fc.value + FROM (SELECT f.filter + FROM {filter_active} f + JOIN {context} ctx ON f.contextid = ctx.id + WHERE ctx.id IN ($contextids) + GROUP BY filter + HAVING MAX(f.active * ctx.depth) > -MIN(f.active * ctx.depth) + ORDER BY MAX(f.sortorder)) active + LEFT JOIN {filter_config} fc ON fc.filter = active.filter + WHERE fc.contextid = $context->id OR fc.contextid IS NULL"); + + // Masssage the data into the specified format to return. + $filters = array(); + foreach ($rs as $row) { + if (!isset($filters[$row->filter])) { + $filters[$row->filter] = array(); + } + if (!is_null($row->name)) { + $filters[$row->filter][$row->name] = $row->value; + } + } + + $rs->close(); + + return $filters; +} + +/** * Process phrases intelligently found within a HTML text (such as adding links) * * param text the text that we are filtering diff --git a/lib/simpletest/testfilterconfig.php b/lib/simpletest/testfilterconfig.php index 5603c31..1237fbb 100644 --- a/lib/simpletest/testfilterconfig.php +++ b/lib/simpletest/testfilterconfig.php @@ -341,4 +341,140 @@ class filter_config_test extends UnitTestCaseUsingDatabase { $this->assertEqual(array('setting1' => 'An arbitrary value', 'setting2' => 'Another arbitrary value'), $config); } } + +class get_active_filters_test extends UnitTestCaseUsingDatabase { + private $syscontext; + private $childcontext; + private $childcontext2; + + public function 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'), 'lib'); + $this->switch_to_test_db(); + + // Set up systcontext in the test database. + $this->testdb->insert_record('context', $this->syscontext); + $this->syscontext->id = 1; + + // Set up a child context. + $this->childcontext = new stdClass; + $this->childcontext->contextlevel = CONTEXT_COURSECAT; + $this->childcontext->instanceid = 1; + $this->childcontext->depth = 2; + $this->childcontext->path = '/1/2'; + $this->testdb->insert_record('context', $this->childcontext); + $this->childcontext->id = 2; + + // Set up a grandchild context. + $this->childcontext2 = new stdClass; + $this->childcontext2->contextlevel = CONTEXT_COURSE; + $this->childcontext2->instanceid = 2; + $this->childcontext2->depth = 3; + $this->childcontext2->path = '/1/2/3'; + $this->testdb->insert_record('context', $this->childcontext2); + $this->childcontext2->id = 3; + } + + private function assert_filter_list($expectedfilters, $filters) { + $this->assert(new ArraysHaveSameValuesExpectation($expectedfilters), array_keys($filters)); + } + + public function test_globally_on_is_returned() { + // Setup fixture. + filter_set_global_state('filter/name', TEXTFILTER_ON); + // Exercise SUT. + $filters = get_active_filters($this->syscontext); + // Validate. + $this->assert_filter_list(array('filter/name'), $filters); + // Check no config returned correctly. + $this->assertEqual(array(), $filters['filter/name']); + } + + public function test_globally_off_not_returned() { + // Setup fixture. + filter_set_global_state('filter/name', TEXTFILTER_OFF); + // Exercise SUT. + $filters = get_active_filters($this->childcontext2); + // Validate. + $this->assert_filter_list(array(), $filters); + } + + public function test_globally_off_overridden() { + // Setup fixture. + filter_set_global_state('filter/name', TEXTFILTER_OFF); + filter_set_local_state('filter/name', $this->childcontext->id, TEXTFILTER_ON); + // Exercise SUT. + $filters = get_active_filters($this->childcontext2); + // Validate. + $this->assert_filter_list(array('filter/name'), $filters); + } + + public function test_globally_on_overridden() { + // Setup fixture. + filter_set_global_state('filter/name', TEXTFILTER_ON); + filter_set_local_state('filter/name', $this->childcontext->id, TEXTFILTER_OFF); + // Exercise SUT. + $filters = get_active_filters($this->childcontext2); + // Validate. + $this->assert_filter_list(array(), $filters); + } + + public function test_globally_disabled_not_overridden() { + // Setup fixture. + filter_set_global_state('filter/name', TEXTFILTER_DISABLED); + filter_set_local_state('filter/name', $this->childcontext->id, TEXTFILTER_ON); + // Exercise SUT. + $filters = get_active_filters($this->syscontext); + // Validate. + $this->assert_filter_list(array(), $filters); + } + + public function test_single_config_returned() { + // Setup fixture. + filter_set_global_state('filter/name', TEXTFILTER_ON); + filter_set_local_config('filter/name', $this->childcontext->id, 'settingname', 'A value'); + // Exercise SUT. + $filters = get_active_filters($this->childcontext); + // Validate. + $this->assertEqual(array('settingname' => 'A value'), $filters['filter/name']); + } + + public function test_multi_config_returned() { + // Setup fixture. + filter_set_global_state('filter/name', TEXTFILTER_ON); + filter_set_local_config('filter/name', $this->childcontext->id, 'settingname', 'A value'); + filter_set_local_config('filter/name', $this->childcontext->id, 'anothersettingname', 'Another value'); + // Exercise SUT. + $filters = get_active_filters($this->childcontext); + // Validate. + $this->assertEqual(array('settingname' => 'A value', 'anothersettingname' => 'Another value'), $filters['filter/name']); + } + + public function test_config_from_other_context_not_returned() { + // Setup fixture. + filter_set_global_state('filter/name', TEXTFILTER_ON); + filter_set_local_config('filter/name', $this->childcontext->id, 'settingname', 'A value'); + filter_set_local_config('filter/name', $this->childcontext2->id, 'anothersettingname', 'Another value'); + // Exercise SUT. + $filters = get_active_filters($this->childcontext2); + // Validate. + $this->assertEqual(array('anothersettingname' => 'Another value'), $filters['filter/name']); + } + + public function test_config_from_other_filter_not_returned() { + // Setup fixture. + filter_set_global_state('filter/name', TEXTFILTER_ON); + filter_set_local_config('filter/name', $this->childcontext->id, 'settingname', 'A value'); + filter_set_local_config('filter/other', $this->childcontext->id, 'anothersettingname', 'Another value'); + // Exercise SUT. + $filters = get_active_filters($this->childcontext); + // Validate. + $this->assertEqual(array('settingname' => 'A value'), $filters['filter/name']); + } +} + ?> -- 1.5.6.3 From 52d371f0038d1aa169e1f540fc261d4a82c8b72b Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Fri, 3 Apr 2009 19:38:33 +0800 Subject: filters: MDL-7336 create new moodle/filter:manage capability --- lang/en_utf8/role.php | 1 + lib/db/access.php | 11 +++++++++++ version.php | 2 +- 3 files changed, 13 insertions(+), 1 deletions(-) diff --git a/lang/en_utf8/role.php b/lang/en_utf8/role.php index 71e8e6c..83ec50a 100644 --- a/lang/en_utf8/role.php +++ b/lang/en_utf8/role.php @@ -103,6 +103,7 @@ $string['explainpermissionsinfo'] = '

To use this table:

  1. First look $string['explainpermissionsdoanything'] = 'Note that this user has the moodle/site:doanything capability, so even though the table above shows that has_capability will return false, this user will actually be deemed to have the capability $a in most circumstances.'; $string['extusers'] = 'Existing users'; $string['extusersmatching'] = 'Existing users matching \'$a\''; +$string['filter:manage'] = 'Manage local filter settings'; $string['globalrole'] = 'System role'; $string['globalroleswarning'] = 'WARNING! Any roles you assign from this page will apply to the assigned users throughout the entire system, including the front page and all the courses.'; $string['gotoassignroles'] = 'Go to Assign roles for this $a->contextlevel'; diff --git a/lib/db/access.php b/lib/db/access.php index 341c499..292e34c 100644 --- a/lib/db/access.php +++ b/lib/db/access.php @@ -300,6 +300,17 @@ $moodle_capabilities = array( ) ), + // Permission to manage filter setting overrides in subcontexts. + 'moodle/filter:manage' => array( + + 'captype' => 'write', + 'contextlevel' => CONTEXT_COURSE, + 'legacy' => array( + 'editingteacher' => CAP_ALLOW, + 'coursecreator' => CAP_ALLOW, + 'admin' => CAP_ALLOW, + ) + ), 'moodle/user:create' => array( diff --git a/version.php b/version.php index 8f0e3d0..3191aa2 100644 --- a/version.php +++ b/version.php @@ -6,7 +6,7 @@ // This is compared against the values stored in the database to determine // whether upgrades should be performed (see lib/db/*.php) - $version = 2009040302; // YYYYMMDD = date of the last version bump + $version = 2009040303; // YYYYMMDD = date of the last version bump // XX = daily increments $release = '2.0 dev (Build: 20090403)'; // Human-friendly version name -- 1.5.6.3