1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Tests for the parts of ../filterlib.php that involve loading the configuration
19 * from, and saving the configuration to, the database.
20 *
21 * @package   core_filter
22 * @category  phpunit
23 * @copyright 2009 Tim Hunt
24 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 */
26
27defined('MOODLE_INTERNAL') || die();
28
29global $CFG;
30require_once($CFG->libdir . '/filterlib.php');
31
32/**
33 * Test filters.
34 */
35class core_filterlib_testcase extends advanced_testcase {
36
37    private function assert_only_one_filter_globally($filter, $state) {
38        global $DB;
39        $recs = $DB->get_records('filter_active');
40        $this->assertCount(1, $recs);
41        $rec = reset($recs);
42        unset($rec->id);
43        $expectedrec = new stdClass();
44        $expectedrec->filter = $filter;
45        $expectedrec->contextid = context_system::instance()->id;
46        $expectedrec->active = $state;
47        $expectedrec->sortorder = 1;
48        $this->assertEquals($expectedrec, $rec);
49    }
50
51    private function assert_global_sort_order($filters) {
52        global $DB;
53
54        $sortedfilters = $DB->get_records_menu('filter_active',
55            array('contextid' => context_system::instance()->id), 'sortorder', 'sortorder,filter');
56        $testarray = array();
57        $index = 1;
58        foreach ($filters as $filter) {
59            $testarray[$index++] = $filter;
60        }
61        $this->assertEquals($testarray, $sortedfilters);
62    }
63
64    public function test_set_filter_globally_on() {
65        $this->resetAfterTest();
66        $this->remove_all_filters_from_config(); // Remove all filters.
67        // Setup fixture.
68        // Exercise SUT.
69        filter_set_global_state('name', TEXTFILTER_ON);
70        // Validate.
71        $this->assert_only_one_filter_globally('name', TEXTFILTER_ON);
72    }
73
74    public function test_set_filter_globally_off() {
75        $this->resetAfterTest();
76        $this->remove_all_filters_from_config(); // Remove all filters.
77        // Setup fixture.
78        // Exercise SUT.
79        filter_set_global_state('name', TEXTFILTER_OFF);
80        // Validate.
81        $this->assert_only_one_filter_globally('name', TEXTFILTER_OFF);
82    }
83
84    public function test_set_filter_globally_disabled() {
85        $this->resetAfterTest();
86        $this->remove_all_filters_from_config(); // Remove all filters.
87        // Setup fixture.
88        // Exercise SUT.
89        filter_set_global_state('name', TEXTFILTER_DISABLED);
90        // Validate.
91        $this->assert_only_one_filter_globally('name', TEXTFILTER_DISABLED);
92    }
93
94    public function test_global_config_exception_on_invalid_state() {
95        $this->resetAfterTest();
96        $this->expectException(coding_exception::class);
97        filter_set_global_state('name', 0);
98    }
99
100    public function test_auto_sort_order() {
101        $this->resetAfterTest();
102        $this->remove_all_filters_from_config(); // Remove all filters.
103        // Setup fixture.
104        // Exercise SUT.
105        filter_set_global_state('one', TEXTFILTER_DISABLED);
106        filter_set_global_state('two', TEXTFILTER_DISABLED);
107        // Validate.
108        $this->assert_global_sort_order(array('one', 'two'));
109    }
110
111    public function test_auto_sort_order_enabled() {
112        $this->resetAfterTest();
113        $this->remove_all_filters_from_config(); // Remove all filters.
114        // Setup fixture.
115        // Exercise SUT.
116        filter_set_global_state('one', TEXTFILTER_ON);
117        filter_set_global_state('two', TEXTFILTER_OFF);
118        // Validate.
119        $this->assert_global_sort_order(array('one', 'two'));
120    }
121
122    public function test_update_existing_dont_duplicate() {
123        $this->resetAfterTest();
124        $this->remove_all_filters_from_config(); // Remove all filters.
125        // Setup fixture.
126        // Exercise SUT.
127        filter_set_global_state('name', TEXTFILTER_ON);
128        filter_set_global_state('name', TEXTFILTER_OFF);
129        // Validate.
130        $this->assert_only_one_filter_globally('name', TEXTFILTER_OFF);
131    }
132
133    public function test_update_reorder_down() {
134        $this->resetAfterTest();
135        $this->remove_all_filters_from_config(); // Remove all filters.
136        // Setup fixture.
137        filter_set_global_state('one', TEXTFILTER_ON);
138        filter_set_global_state('two', TEXTFILTER_ON);
139        filter_set_global_state('three', TEXTFILTER_ON);
140        // Exercise SUT.
141        filter_set_global_state('two', TEXTFILTER_ON, -1);
142        // Validate.
143        $this->assert_global_sort_order(array('two', 'one', 'three'));
144    }
145
146    public function test_update_reorder_up() {
147        $this->resetAfterTest();
148        $this->remove_all_filters_from_config(); // Remove all filters.
149        // Setup fixture.
150        filter_set_global_state('one', TEXTFILTER_ON);
151        filter_set_global_state('two', TEXTFILTER_ON);
152        filter_set_global_state('three', TEXTFILTER_ON);
153        filter_set_global_state('four', TEXTFILTER_ON);
154        // Exercise SUT.
155        filter_set_global_state('two', TEXTFILTER_ON, 1);
156        // Validate.
157        $this->assert_global_sort_order(array('one', 'three', 'two', 'four'));
158    }
159
160    public function test_auto_sort_order_change_to_enabled() {
161        $this->resetAfterTest();
162        $this->remove_all_filters_from_config(); // Remove all filters.
163        // Setup fixture.
164        filter_set_global_state('one', TEXTFILTER_ON);
165        filter_set_global_state('two', TEXTFILTER_DISABLED);
166        filter_set_global_state('three', TEXTFILTER_DISABLED);
167        // Exercise SUT.
168        filter_set_global_state('three', TEXTFILTER_ON);
169        // Validate.
170        $this->assert_global_sort_order(array('one', 'three', 'two'));
171    }
172
173    public function test_auto_sort_order_change_to_disabled() {
174        $this->resetAfterTest();
175        $this->remove_all_filters_from_config(); // Remove all filters.
176        // Setup fixture.
177        filter_set_global_state('one', TEXTFILTER_ON);
178        filter_set_global_state('two', TEXTFILTER_ON);
179        filter_set_global_state('three', TEXTFILTER_DISABLED);
180        // Exercise SUT.
181        filter_set_global_state('one', TEXTFILTER_DISABLED);
182        // Validate.
183        $this->assert_global_sort_order(array('two', 'one', 'three'));
184    }
185
186    public function test_filter_get_global_states() {
187        $this->resetAfterTest();
188        $this->remove_all_filters_from_config(); // Remove all filters.
189        // Setup fixture.
190        filter_set_global_state('one', TEXTFILTER_ON);
191        filter_set_global_state('two', TEXTFILTER_OFF);
192        filter_set_global_state('three', TEXTFILTER_DISABLED);
193        // Exercise SUT.
194        $filters = filter_get_global_states();
195        // Validate.
196        $this->assertEquals(array(
197            'one' => (object) array('filter' => 'one', 'active' => TEXTFILTER_ON, 'sortorder' => 1),
198            'two' => (object) array('filter' => 'two', 'active' => TEXTFILTER_OFF, 'sortorder' => 2),
199            'three' => (object) array('filter' => 'three', 'active' => TEXTFILTER_DISABLED, 'sortorder' => 3)
200        ), $filters);
201    }
202
203    private function assert_only_one_local_setting($filter, $contextid, $state) {
204        global $DB;
205        $recs = $DB->get_records('filter_active');
206        $this->assertEquals(1, count($recs), 'More than one record returned %s.');
207        $rec = reset($recs);
208        unset($rec->id);
209        unset($rec->sortorder);
210        $expectedrec = new stdClass();
211        $expectedrec->filter = $filter;
212        $expectedrec->contextid = $contextid;
213        $expectedrec->active = $state;
214        $this->assertEquals($expectedrec, $rec);
215    }
216
217    private function assert_no_local_setting() {
218        global $DB;
219        $this->assertEquals(0, $DB->count_records('filter_active'));
220    }
221
222    public function test_local_on() {
223        $this->resetAfterTest();
224        $this->remove_all_filters_from_config(); // Remove all filters.
225        // Exercise SUT.
226        filter_set_local_state('name', 123, TEXTFILTER_ON);
227        // Validate.
228        $this->assert_only_one_local_setting('name', 123, TEXTFILTER_ON);
229    }
230
231    public function test_local_off() {
232        $this->resetAfterTest();
233        $this->remove_all_filters_from_config(); // Remove all filters.
234        // Exercise SUT.
235        filter_set_local_state('name', 123, TEXTFILTER_OFF);
236        // Validate.
237        $this->assert_only_one_local_setting('name', 123, TEXTFILTER_OFF);
238    }
239
240    public function test_local_inherit() {
241        $this->resetAfterTest();
242        $this->remove_all_filters_from_config(); // Remove all filters.
243        // Exercise SUT.
244        filter_set_local_state('name', 123, TEXTFILTER_INHERIT);
245        // Validate.
246        $this->assert_no_local_setting();
247    }
248
249    public function test_local_invalid_state_throws_exception() {
250        $this->resetAfterTest();
251        // Exercise SUT.
252        $this->expectException(coding_exception::class);
253        filter_set_local_state('name', 123, -9999);
254    }
255
256    public function test_throws_exception_when_setting_global() {
257        $this->resetAfterTest();
258        // Exercise SUT.
259        $this->expectException(coding_exception::class);
260        filter_set_local_state('name', context_system::instance()->id, TEXTFILTER_INHERIT);
261    }
262
263    public function test_local_inherit_deletes_existing() {
264        $this->resetAfterTest();
265        $this->remove_all_filters_from_config(); // Remove all filters.
266        // Setup fixture.
267        filter_set_local_state('name', 123, TEXTFILTER_INHERIT);
268        // Exercise SUT.
269        filter_set_local_state('name', 123, TEXTFILTER_INHERIT);
270        // Validate.
271        $this->assert_no_local_setting();
272    }
273
274    private function assert_only_one_config($filter, $context, $name, $value) {
275        global $DB;
276        $recs = $DB->get_records('filter_config');
277        $this->assertEquals(1, count($recs), 'More than one record returned %s.');
278        $rec = reset($recs);
279        unset($rec->id);
280        $expectedrec = new stdClass();
281        $expectedrec->filter = $filter;
282        $expectedrec->contextid = $context;
283        $expectedrec->name = $name;
284        $expectedrec->value = $value;
285        $this->assertEquals($expectedrec, $rec);
286    }
287
288    public function test_set_new_config() {
289        $this->resetAfterTest();
290        $this->remove_all_filters_from_config(); // Remove all filters.
291        // Exercise SUT.
292        filter_set_local_config('name', 123, 'settingname', 'An arbitrary value');
293        // Validate.
294        $this->assert_only_one_config('name', 123, 'settingname', 'An arbitrary value');
295    }
296
297    public function test_update_existing_config() {
298        $this->resetAfterTest();
299        $this->remove_all_filters_from_config(); // Remove all filters.
300        // Setup fixture.
301        filter_set_local_config('name', 123, 'settingname', 'An arbitrary value');
302        // Exercise SUT.
303        filter_set_local_config('name', 123, 'settingname', 'A changed value');
304        // Validate.
305        $this->assert_only_one_config('name', 123, 'settingname', 'A changed value');
306    }
307
308    public function test_filter_get_local_config() {
309        $this->resetAfterTest();
310        // Setup fixture.
311        filter_set_local_config('name', 123, 'setting1', 'An arbitrary value');
312        filter_set_local_config('name', 123, 'setting2', 'Another arbitrary value');
313        filter_set_local_config('name', 122, 'settingname', 'Value from another context');
314        filter_set_local_config('other', 123, 'settingname', 'Someone else\'s value');
315        // Exercise SUT.
316        $config = filter_get_local_config('name', 123);
317        // Validate.
318        $this->assertEquals(array('setting1' => 'An arbitrary value', 'setting2' => 'Another arbitrary value'), $config);
319    }
320
321    protected function setup_available_in_context_tests() {
322        $course = $this->getDataGenerator()->create_course(array('category' => 1));
323
324        $childcontext = context_coursecat::instance(1);
325        $childcontext2 = context_course::instance($course->id);
326        $syscontext = context_system::instance();
327
328        return [
329            'syscontext' => $syscontext,
330            'childcontext' => $childcontext,
331            'childcontext2' => $childcontext2
332        ];
333    }
334
335    protected function remove_all_filters_from_config() {
336        global $DB;
337        $DB->delete_records('filter_active', array());
338        $DB->delete_records('filter_config', array());
339    }
340
341    private function assert_filter_list($expectedfilters, $filters) {
342        $this->assertEqualsCanonicalizing($expectedfilters, array_keys($filters));
343    }
344
345    public function test_globally_on_is_returned() {
346        $this->resetAfterTest();
347        $this->remove_all_filters_from_config(); // Remove all filters.
348        [
349            'syscontext' => $syscontext
350        ] = $this->setup_available_in_context_tests();
351        // Setup fixture.
352        filter_set_global_state('name', TEXTFILTER_ON);
353        // Exercise SUT.
354        $filters = filter_get_active_in_context($syscontext);
355        // Validate.
356        $this->assert_filter_list(array('name'), $filters);
357        // Check no config returned correctly.
358        $this->assertEquals(array(), $filters['name']);
359    }
360
361    public function test_globally_off_not_returned() {
362        $this->resetAfterTest();
363        $this->remove_all_filters_from_config(); // Remove all filters.
364        [
365            'childcontext2' => $childcontext2
366        ] = $this->setup_available_in_context_tests();
367        // Setup fixture.
368        filter_set_global_state('name', TEXTFILTER_OFF);
369        // Exercise SUT.
370        $filters = filter_get_active_in_context($childcontext2);
371        // Validate.
372        $this->assert_filter_list(array(), $filters);
373    }
374
375    public function test_globally_off_overridden() {
376        $this->resetAfterTest();
377        $this->remove_all_filters_from_config(); // Remove all filters.
378        [
379            'childcontext' => $childcontext,
380            'childcontext2' => $childcontext2
381        ] = $this->setup_available_in_context_tests();
382        // Setup fixture.
383        filter_set_global_state('name', TEXTFILTER_OFF);
384        filter_set_local_state('name', $childcontext->id, TEXTFILTER_ON);
385        // Exercise SUT.
386        $filters = filter_get_active_in_context($childcontext2);
387        // Validate.
388        $this->assert_filter_list(array('name'), $filters);
389    }
390
391    public function test_globally_on_overridden() {
392        $this->resetAfterTest();
393        $this->remove_all_filters_from_config(); // Remove all filters.
394        [
395            'childcontext' => $childcontext,
396            'childcontext2' => $childcontext2
397        ] = $this->setup_available_in_context_tests();
398        // Setup fixture.
399        filter_set_global_state('name', TEXTFILTER_ON);
400        filter_set_local_state('name', $childcontext->id, TEXTFILTER_OFF);
401        // Exercise SUT.
402        $filters = filter_get_active_in_context($childcontext2);
403        // Validate.
404        $this->assert_filter_list(array(), $filters);
405    }
406
407    public function test_globally_disabled_not_overridden() {
408        $this->resetAfterTest();
409        $this->remove_all_filters_from_config(); // Remove all filters.
410        [
411            'syscontext' => $syscontext,
412            'childcontext' => $childcontext
413        ] = $this->setup_available_in_context_tests();
414        // Setup fixture.
415        filter_set_global_state('name', TEXTFILTER_DISABLED);
416        filter_set_local_state('name', $childcontext->id, TEXTFILTER_ON);
417        // Exercise SUT.
418        $filters = filter_get_active_in_context($syscontext);
419        // Validate.
420        $this->assert_filter_list(array(), $filters);
421    }
422
423    public function test_single_config_returned() {
424        $this->resetAfterTest();
425        [
426            'childcontext' => $childcontext
427        ] = $this->setup_available_in_context_tests();
428        // Setup fixture.
429        filter_set_global_state('name', TEXTFILTER_ON);
430        filter_set_local_config('name', $childcontext->id, 'settingname', 'A value');
431        // Exercise SUT.
432        $filters = filter_get_active_in_context($childcontext);
433        // Validate.
434        $this->assertEquals(array('settingname' => 'A value'), $filters['name']);
435    }
436
437    public function test_multi_config_returned() {
438        $this->resetAfterTest();
439        [
440            'childcontext' => $childcontext
441        ] = $this->setup_available_in_context_tests();
442        // Setup fixture.
443        filter_set_global_state('name', TEXTFILTER_ON);
444        filter_set_local_config('name', $childcontext->id, 'settingname', 'A value');
445        filter_set_local_config('name', $childcontext->id, 'anothersettingname', 'Another value');
446        // Exercise SUT.
447        $filters = filter_get_active_in_context($childcontext);
448        // Validate.
449        $this->assertEquals(array('settingname' => 'A value', 'anothersettingname' => 'Another value'), $filters['name']);
450    }
451
452    public function test_config_from_other_context_not_returned() {
453        $this->resetAfterTest();
454        [
455            'childcontext' => $childcontext,
456            'childcontext2' => $childcontext2
457        ] = $this->setup_available_in_context_tests();
458        // Setup fixture.
459        filter_set_global_state('name', TEXTFILTER_ON);
460        filter_set_local_config('name', $childcontext->id, 'settingname', 'A value');
461        filter_set_local_config('name', $childcontext2->id, 'anothersettingname', 'Another value');
462        // Exercise SUT.
463        $filters = filter_get_active_in_context($childcontext2);
464        // Validate.
465        $this->assertEquals(array('anothersettingname' => 'Another value'), $filters['name']);
466    }
467
468    public function test_config_from_other_filter_not_returned() {
469        $this->resetAfterTest();
470        [
471            'childcontext' => $childcontext
472        ] = $this->setup_available_in_context_tests();
473        // Setup fixture.
474        filter_set_global_state('name', TEXTFILTER_ON);
475        filter_set_local_config('name', $childcontext->id, 'settingname', 'A value');
476        filter_set_local_config('other', $childcontext->id, 'anothersettingname', 'Another value');
477        // Exercise SUT.
478        $filters = filter_get_active_in_context($childcontext);
479        // Validate.
480        $this->assertEquals(array('settingname' => 'A value'), $filters['name']);
481    }
482
483    protected function assert_one_available_filter($filter, $localstate, $inheritedstate, $filters) {
484        $this->assertEquals(1, count($filters), 'More than one record returned %s.');
485        $rec = $filters[$filter];
486        unset($rec->id);
487        $expectedrec = new stdClass();
488        $expectedrec->filter = $filter;
489        $expectedrec->localstate = $localstate;
490        $expectedrec->inheritedstate = $inheritedstate;
491        $this->assertEquals($expectedrec, $rec);
492    }
493
494    public function test_available_in_context_localoverride() {
495        $this->resetAfterTest();
496        $this->remove_all_filters_from_config(); // Remove all filters.
497        [
498            'childcontext' => $childcontext
499        ] = $this->setup_available_in_context_tests();
500        // Setup fixture.
501        filter_set_global_state('name', TEXTFILTER_ON);
502        filter_set_local_state('name', $childcontext->id, TEXTFILTER_OFF);
503        // Exercise SUT.
504        $filters = filter_get_available_in_context($childcontext);
505        // Validate.
506        $this->assert_one_available_filter('name', TEXTFILTER_OFF, TEXTFILTER_ON, $filters);
507    }
508
509    public function test_available_in_context_nolocaloverride() {
510        $this->resetAfterTest();
511        $this->remove_all_filters_from_config(); // Remove all filters.
512        [
513            'childcontext' => $childcontext,
514            'childcontext2' => $childcontext2
515        ] = $this->setup_available_in_context_tests();
516        // Setup fixture.
517        filter_set_global_state('name', TEXTFILTER_ON);
518        filter_set_local_state('name', $childcontext->id, TEXTFILTER_OFF);
519        // Exercise SUT.
520        $filters = filter_get_available_in_context($childcontext2);
521        // Validate.
522        $this->assert_one_available_filter('name', TEXTFILTER_INHERIT, TEXTFILTER_OFF, $filters);
523    }
524
525    public function test_available_in_context_disabled_not_returned() {
526        $this->resetAfterTest();
527        $this->remove_all_filters_from_config(); // Remove all filters.
528        [
529            'childcontext' => $childcontext
530        ] = $this->setup_available_in_context_tests();
531        // Setup fixture.
532        filter_set_global_state('name', TEXTFILTER_DISABLED);
533        filter_set_local_state('name', $childcontext->id, TEXTFILTER_ON);
534        // Exercise SUT.
535        $filters = filter_get_available_in_context($childcontext);
536        // Validate.
537        $this->assertEquals(array(), $filters);
538    }
539
540    public function test_available_in_context_exception_with_syscontext() {
541        $this->resetAfterTest();
542        [
543            'syscontext' => $syscontext
544        ] = $this->setup_available_in_context_tests();
545        // Exercise SUT.
546        $this->expectException(coding_exception::class);
547        filter_get_available_in_context($syscontext);
548    }
549
550    protected function setup_preload_activities_test() {
551        $syscontext = context_system::instance();
552        $catcontext = context_coursecat::instance(1);
553        $course = $this->getDataGenerator()->create_course(array('category' => 1));
554        $coursecontext = context_course::instance($course->id);
555        $page1 = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
556        $activity1context = context_module::instance($page1->cmid);
557        $page2 = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
558        $activity2context = context_module::instance($page2->cmid);
559        return [
560            'syscontext' => $syscontext,
561            'catcontext' => $catcontext,
562            'course' => $course,
563            'coursecontext' => $coursecontext,
564            'activity1context' => $activity1context,
565            'activity2context' => $activity2context
566         ];
567    }
568
569    private function assert_matches($modinfo, $activity1context, $activity2context) {
570        global $FILTERLIB_PRIVATE, $DB;
571
572        // Use preload cache...
573        $FILTERLIB_PRIVATE = new stdClass();
574        filter_preload_activities($modinfo);
575
576        // Get data and check no queries are made.
577        $before = $DB->perf_get_reads();
578        $plfilters1 = filter_get_active_in_context($activity1context);
579        $plfilters2 = filter_get_active_in_context($activity2context);
580        $after = $DB->perf_get_reads();
581        $this->assertEquals($before, $after);
582
583        // Repeat without cache and check it makes queries now.
584        $FILTERLIB_PRIVATE = new stdClass;
585        $before = $DB->perf_get_reads();
586        $filters1 = filter_get_active_in_context($activity1context);
587        $filters2 = filter_get_active_in_context($activity2context);
588        $after = $DB->perf_get_reads();
589        $this->assertTrue($after > $before);
590
591        // Check they match.
592        $this->assertEquals($plfilters1, $filters1);
593        $this->assertEquals($plfilters2, $filters2);
594    }
595
596    public function test_preload() {
597        $this->resetAfterTest();
598        [
599            'catcontext' => $catcontext,
600            'course' => $course,
601            'coursecontext' => $coursecontext,
602            'activity1context' => $activity1context,
603            'activity2context' => $activity2context
604         ] = $this->setup_preload_activities_test();
605        // Get course and modinfo.
606        $modinfo = new course_modinfo($course, 2);
607
608        // Note: All the tests in this function check that the result from the
609        // preloaded cache is the same as the result from calling the standard
610        // function without preloading.
611
612        // Initially, check with no filters enabled.
613        $this->assert_matches($modinfo, $activity1context, $activity2context);
614
615        // Enable filter globally, check.
616        filter_set_global_state('name', TEXTFILTER_ON);
617        $this->assert_matches($modinfo, $activity1context, $activity2context);
618
619        // Disable for activity 2.
620        filter_set_local_state('name', $activity2context->id, TEXTFILTER_OFF);
621        $this->assert_matches($modinfo, $activity1context, $activity2context);
622
623        // Disable at category.
624        filter_set_local_state('name', $catcontext->id, TEXTFILTER_OFF);
625        $this->assert_matches($modinfo, $activity1context, $activity2context);
626
627        // Enable for activity 1.
628        filter_set_local_state('name', $activity1context->id, TEXTFILTER_ON);
629        $this->assert_matches($modinfo, $activity1context, $activity2context);
630
631        // Disable globally.
632        filter_set_global_state('name', TEXTFILTER_DISABLED);
633        $this->assert_matches($modinfo, $activity1context, $activity2context);
634
635        // Add another 2 filters.
636        filter_set_global_state('frog', TEXTFILTER_ON);
637        filter_set_global_state('zombie', TEXTFILTER_ON);
638        $this->assert_matches($modinfo, $activity1context, $activity2context);
639
640        // Disable random one of these in each context.
641        filter_set_local_state('zombie', $activity1context->id, TEXTFILTER_OFF);
642        filter_set_local_state('frog', $activity2context->id, TEXTFILTER_OFF);
643        $this->assert_matches($modinfo, $activity1context, $activity2context);
644
645        // Now do some filter options.
646        filter_set_local_config('name', $activity1context->id, 'a', 'x');
647        filter_set_local_config('zombie', $activity1context->id, 'a', 'y');
648        filter_set_local_config('frog', $activity1context->id, 'a', 'z');
649        // These last two don't do anything as they are not at final level but I
650        // thought it would be good to have that verified in test.
651        filter_set_local_config('frog', $coursecontext->id, 'q', 'x');
652        filter_set_local_config('frog', $catcontext->id, 'q', 'z');
653        $this->assert_matches($modinfo, $activity1context, $activity2context);
654    }
655
656    public function test_filter_delete_all_for_filter() {
657        global $DB;
658        $this->resetAfterTest();
659        $this->remove_all_filters_from_config(); // Remove all filters.
660
661        // Setup fixture.
662        filter_set_global_state('name', TEXTFILTER_ON);
663        filter_set_global_state('other', TEXTFILTER_ON);
664        filter_set_local_config('name', context_system::instance()->id, 'settingname', 'A value');
665        filter_set_local_config('other', context_system::instance()->id, 'settingname', 'Other value');
666        set_config('configname', 'A config value', 'filter_name');
667        set_config('configname', 'Other config value', 'filter_other');
668        // Exercise SUT.
669        filter_delete_all_for_filter('name');
670        // Validate.
671        $this->assertEquals(1, $DB->count_records('filter_active'));
672        $this->assertTrue($DB->record_exists('filter_active', array('filter' => 'other')));
673        $this->assertEquals(1, $DB->count_records('filter_config'));
674        $this->assertTrue($DB->record_exists('filter_config', array('filter' => 'other')));
675        $expectedconfig = new stdClass;
676        $expectedconfig->configname = 'Other config value';
677        $this->assertEquals($expectedconfig, get_config('filter_other'));
678        $this->assertEquals(get_config('filter_name'), new stdClass());
679    }
680
681    public function test_filter_delete_all_for_context() {
682        global $DB;
683        $this->resetAfterTest();
684        $this->remove_all_filters_from_config(); // Remove all filters.
685
686        // Setup fixture.
687        filter_set_global_state('name', TEXTFILTER_ON);
688        filter_set_local_state('name', 123, TEXTFILTER_OFF);
689        filter_set_local_config('name', 123, 'settingname', 'A value');
690        filter_set_local_config('other', 123, 'settingname', 'Other value');
691        filter_set_local_config('other', 122, 'settingname', 'Other value');
692        // Exercise SUT.
693        filter_delete_all_for_context(123);
694        // Validate.
695        $this->assertEquals(1, $DB->count_records('filter_active'));
696        $this->assertTrue($DB->record_exists('filter_active', array('contextid' => context_system::instance()->id)));
697        $this->assertEquals(1, $DB->count_records('filter_config'));
698        $this->assertTrue($DB->record_exists('filter_config', array('filter' => 'other')));
699    }
700
701    public function test_set() {
702        global $CFG;
703        $this->resetAfterTest();
704
705        $this->assertFileExists("$CFG->dirroot/filter/emailprotect"); // Any standard filter.
706        $this->assertFileExists("$CFG->dirroot/filter/tidy");         // Any standard filter.
707        $this->assertFileDoesNotExist("$CFG->dirroot/filter/grgrggr");   // Any non-existent filter.
708
709        // Setup fixture.
710        set_config('filterall', 0);
711        set_config('stringfilters', '');
712        // Exercise SUT.
713        filter_set_applies_to_strings('tidy', true);
714        // Validate.
715        $this->assertEquals('tidy', $CFG->stringfilters);
716        $this->assertEquals(1, $CFG->filterall);
717
718        filter_set_applies_to_strings('grgrggr', true);
719        $this->assertEquals('tidy', $CFG->stringfilters);
720        $this->assertEquals(1, $CFG->filterall);
721
722        filter_set_applies_to_strings('emailprotect', true);
723        $this->assertEquals('tidy,emailprotect', $CFG->stringfilters);
724        $this->assertEquals(1, $CFG->filterall);
725    }
726
727    public function test_unset_to_empty() {
728        global $CFG;
729        $this->resetAfterTest();
730
731        $this->assertFileExists("$CFG->dirroot/filter/tidy"); // Any standard filter.
732
733        // Setup fixture.
734        set_config('filterall', 1);
735        set_config('stringfilters', 'tidy');
736        // Exercise SUT.
737        filter_set_applies_to_strings('tidy', false);
738        // Validate.
739        $this->assertEquals('', $CFG->stringfilters);
740        $this->assertEquals('', $CFG->filterall);
741    }
742
743    public function test_unset_multi() {
744        global $CFG;
745        $this->resetAfterTest();
746
747        $this->assertFileExists("$CFG->dirroot/filter/emailprotect"); // Any standard filter.
748        $this->assertFileExists("$CFG->dirroot/filter/tidy");         // Any standard filter.
749        $this->assertFileExists("$CFG->dirroot/filter/multilang");    // Any standard filter.
750
751        // Setup fixture.
752        set_config('filterall', 1);
753        set_config('stringfilters', 'emailprotect,tidy,multilang');
754        // Exercise SUT.
755        filter_set_applies_to_strings('tidy', false);
756        // Validate.
757        $this->assertEquals('emailprotect,multilang', $CFG->stringfilters);
758        $this->assertEquals(1, $CFG->filterall);
759    }
760
761    public function test_filter_manager_instance() {
762        $this->resetAfterTest();
763
764        set_config('perfdebug', 7);
765        filter_manager::reset_caches();
766        $filterman = filter_manager::instance();
767        $this->assertInstanceOf('filter_manager', $filterman);
768        $this->assertNotInstanceOf('performance_measuring_filter_manager', $filterman);
769
770        set_config('perfdebug', 15);
771        filter_manager::reset_caches();
772        $filterman = filter_manager::instance();
773        $this->assertInstanceOf('filter_manager', $filterman);
774        $this->assertInstanceOf('performance_measuring_filter_manager', $filterman);
775    }
776
777    public function test_filter_get_active_state_contextid_parameter() {
778        $this->resetAfterTest();
779
780        filter_set_global_state('glossary', TEXTFILTER_ON);
781        // Using system context by default.
782        $active = filter_get_active_state('glossary');
783        $this->assertEquals($active, TEXTFILTER_ON);
784
785        $systemcontext = context_system::instance();
786        // Passing $systemcontext object.
787        $active = filter_get_active_state('glossary', $systemcontext);
788        $this->assertEquals($active, TEXTFILTER_ON);
789
790        // Passing $systemcontext id.
791        $active = filter_get_active_state('glossary', $systemcontext->id);
792        $this->assertEquals($active, TEXTFILTER_ON);
793
794        // Not system context.
795        filter_set_local_state('glossary', '123', TEXTFILTER_ON);
796        $active = filter_get_active_state('glossary', '123');
797        $this->assertEquals($active, TEXTFILTER_ON);
798    }
799
800    public function test_filter_get_active_state_filtername_parameter() {
801        $this->resetAfterTest();
802
803        filter_set_global_state('glossary', TEXTFILTER_ON);
804        // Using full filtername.
805        $active = filter_get_active_state('filter/glossary');
806        $this->assertEquals($active, TEXTFILTER_ON);
807
808        // Wrong filtername.
809        $this->expectException('coding_exception');
810        $active = filter_get_active_state('mod/glossary');
811    }
812
813    public function test_filter_get_active_state_after_change() {
814        $this->resetAfterTest();
815
816        filter_set_global_state('glossary', TEXTFILTER_ON);
817        $systemcontextid = context_system::instance()->id;
818        $active = filter_get_active_state('glossary', $systemcontextid);
819        $this->assertEquals($active, TEXTFILTER_ON);
820
821        filter_set_global_state('glossary', TEXTFILTER_OFF);
822        $systemcontextid = context_system::instance()->id;
823        $active = filter_get_active_state('glossary', $systemcontextid);
824        $this->assertEquals($active, TEXTFILTER_OFF);
825
826        filter_set_global_state('glossary', TEXTFILTER_DISABLED);
827        $systemcontextid = context_system::instance()->id;
828        $active = filter_get_active_state('glossary', $systemcontextid);
829        $this->assertEquals($active, TEXTFILTER_DISABLED);
830    }
831
832    public function test_filter_get_globally_enabled_default() {
833        $this->resetAfterTest();
834        $enabledfilters = filter_get_globally_enabled();
835        $this->assertArrayNotHasKey('glossary', $enabledfilters);
836    }
837
838    public function test_filter_get_globally_enabled_after_change() {
839        $this->resetAfterTest();
840        filter_set_global_state('glossary', TEXTFILTER_ON);
841        $enabledfilters = filter_get_globally_enabled();
842        $this->assertArrayHasKey('glossary', $enabledfilters);
843    }
844
845    public function test_filter_get_globally_enabled_filters_with_config() {
846        $this->resetAfterTest();
847        $this->remove_all_filters_from_config(); // Remove all filters.
848        [
849            'syscontext' => $syscontext,
850            'childcontext' => $childcontext
851        ] = $this->setup_available_in_context_tests();
852        $this->remove_all_filters_from_config(); // Remove all filters.
853
854        // Set few filters.
855        filter_set_global_state('one', TEXTFILTER_ON);
856        filter_set_global_state('three', TEXTFILTER_OFF, -1);
857        filter_set_global_state('two', TEXTFILTER_DISABLED);
858
859        // Set global config.
860        filter_set_local_config('one', $syscontext->id, 'test1a', 'In root');
861        filter_set_local_config('one', $syscontext->id, 'test1b', 'In root');
862        filter_set_local_config('two', $syscontext->id, 'test2a', 'In root');
863        filter_set_local_config('two', $syscontext->id, 'test2b', 'In root');
864
865        // Set child config.
866        filter_set_local_config('one', $childcontext->id, 'test1a', 'In child');
867        filter_set_local_config('one', $childcontext->id, 'test1b', 'In child');
868        filter_set_local_config('two', $childcontext->id, 'test2a', 'In child');
869        filter_set_local_config('two', $childcontext->id, 'test2b', 'In child');
870        filter_set_local_config('three', $childcontext->id, 'test3a', 'In child');
871        filter_set_local_config('three', $childcontext->id, 'test3b', 'In child');
872
873        // Check.
874        $actual = filter_get_globally_enabled_filters_with_config();
875        $this->assertCount(2, $actual);
876        $this->assertEquals(['three', 'one'], array_keys($actual));     // Checks sortorder.
877        $this->assertArrayHasKey('one', $actual);
878        $this->assertArrayNotHasKey('two', $actual);
879        $this->assertArrayHasKey('three', $actual);
880        $this->assertEquals(['test1a' => 'In root', 'test1b' => 'In root'], $actual['one']);
881        $this->assertEquals([], $actual['three']);
882    }
883}
884