';
+}
+
+print_footer($course);
+?>
diff --git a/filter/mediaplugin/filter.php b/filter/mediaplugin/filter.php
index 5086fd6..f42f8f7 100644
--- a/filter/mediaplugin/filter.php
+++ b/filter/mediaplugin/filter.php
@@ -17,12 +17,8 @@
require_once($CFG->libdir.'/filelib.php');
-class mediaplugin_filter extends filter_base {
- private $eolas_fix_applied;
- function __construct($courseid, $format, $options) {
- parent::__construct($courseid, $format, $options);
- $this->eolas_fix_applied = false;
- }
+class mediaplugin_filter extends moodle_text_filter {
+ private $eolas_fix_applied = false;
function filter($text) {
global $CFG;
// You should never modify parameters passed to a method or function, it's BAD practice. Create a copy instead.
diff --git a/filter/multilang/filter.php b/filter/multilang/filter.php
index 2f5eb0c..3aa23c1 100644
--- a/filter/multilang/filter.php
+++ b/filter/multilang/filter.php
@@ -36,11 +36,7 @@
// Following new syntax is not compatible with old one:
// one langanother language
-class multilang_filter extends filter_base {
- function __construct($courseid, $format, $options) {
- parent::__construct($courseid, $format, $options);
- }
-
+class multilang_filter extends moodle_text_filter {
function filter($text) {
global $CFG;
diff --git a/filter/tex/filter.php b/filter/tex/filter.php
index 01ee633..c113ec6 100644
--- a/filter/tex/filter.php
+++ b/filter/tex/filter.php
@@ -101,10 +101,7 @@ function string_file_picture_tex($imagefile, $tex= "", $height="", $width="", $a
return $output;
}
-class tex_filter extends filter_base {
- function __construct($courseid, $format, $options) {
- parent::__construct($courseid, $format, $options);
- }
+class tex_filter extends moodle_text_filter {
function filter ($text) {
global $CFG, $DB;
diff --git a/filter/tex/pix.php b/filter/tex/pix.php
index 6c48542..e7240b5 100644
--- a/filter/tex/pix.php
+++ b/filter/tex/pix.php
@@ -7,13 +7,8 @@ define('NO_MOODLE_COOKIES', true); // Because it interferes with caching
require_once('../../config.php');
- if (empty($CFG->textfilters)) {
+ if (!filter_is_enabled('filter/tex')) {
error ('Filter not enabled!');
- } else {
- $filters = explode(',', $CFG->textfilters);
- if (array_search('filter/tex', $filters) === FALSE) {
- error ('Filter not enabled!');
- }
}
// disable moodle specific debug messages
diff --git a/filter/tex/texdebug.php b/filter/tex/texdebug.php
index 917239c..0940b41 100644
--- a/filter/tex/texdebug.php
+++ b/filter/tex/texdebug.php
@@ -5,13 +5,8 @@
require_once("../../config.php");
- if (empty($CFG->textfilters)) {
+ if (!filter_is_enabled('filter/tex')) {
error ('Filter not enabled!');
- } else {
- $filters = explode(',', $CFG->textfilters);
- if (array_search('filter/tex', $filters) === FALSE) {
- error ('Filter not enabled!');
- }
}
require_once($CFG->libdir.'/filelib.php');
diff --git a/filter/tex/texed.php b/filter/tex/texed.php
index 3ac929a..d246192 100644
--- a/filter/tex/texed.php
+++ b/filter/tex/texed.php
@@ -8,13 +8,8 @@
require_once("../../config.php");
require_once($CFG->dirroot.'/filter/tex/lib.php');
- if (empty($CFG->textfilters)) {
+ if (!filter_is_enabled('filter/tex')) {
error ('Filter not enabled!');
- } else {
- $filters = explode(',', $CFG->textfilters);
- if (array_search('filter/tex', $filters) === FALSE) {
- error ('Filter not enabled!');
- }
}
error_reporting(E_ALL);
diff --git a/filter/tidy/filter.php b/filter/tidy/filter.php
index bb6893d..53d1f2d 100644
--- a/filter/tidy/filter.php
+++ b/filter/tidy/filter.php
@@ -11,11 +11,7 @@
// If you want to know what you can set in $tidyoptions and what their default
// values are, see http://php.net/manual/en/function.tidy-get-config.php.
-class tidy_filter extends filter_base {
- function __construct($courseid, $format, $options) {
- parent::__construct($courseid, $format, $options);
- }
-
+class tidy_filter extends moodle_text_filter {
/**
* @author Hannes Gassert
* @param string text to be filtered
diff --git a/lang/en_utf8/admin.php b/lang/en_utf8/admin.php
index da31d7e..86c18d2 100644
--- a/lang/en_utf8/admin.php
+++ b/lang/en_utf8/admin.php
@@ -46,6 +46,7 @@ $string['calendar_weekend'] = 'Weekend Days';
$string['calendarexportsalt'] = 'Calendar export salt';
$string['calendarsettings'] = 'Calendar';
$string['cannotdeletemissingqtype'] = 'You cannot delete the missing question type. It is needed by the system.';
+$string['cannotdeletemodfilter'] = 'You cannot uninstall the \'$a->filter\' because it is part of the \'$a->module\' module.';
$string['cannotdeleteqtypeinuse'] = 'You cannot delete the question type \'$a\'. There are questions of this type in the question bank.';
$string['cannotdeleteqtypeneeded'] = 'You cannot delete the question type \'$a\'. There are other question types installed that rely on it.';
$string['cfgwwwrootwarning'] = 'You have defined $CFG->wwwroot incorrectly in your config.php file. It does not match the URL you are using to access this page. Please correct it, or you will experience strange bugs like MDL-11061.';
@@ -334,11 +335,15 @@ $string['defaultsettinginfo'] = 'Default: $a';
$string['defaultuserroleid'] = 'Default role for all users';
$string['defaultvalues'] = 'Default values';
$string['deleteerrors'] = 'Delete errors';
+$string['deletefilterareyousure'] = 'Are you sure you want to delete the filter \'$a\'';
+$string['deletefilterareyousuremessage'] = 'You are about to completely delete the filter \'$a\'. Are you sure you want to uninstall it?';
+$string['deletefilterfiles'] = 'All data associated with the filter \'$a->filter\' has been deleted from the database. To complete the deletion (and to prevent the filter from re-installing itself), you should now delete this directory from your server: $a->directory';
$string['deleteincompleteusers'] = 'Delete incomplete users after';
$string['deleteqtypeareyousure'] = 'Are you sure you want to delete the question type \'$a\'';
$string['deleteqtypeareyousuremessage'] = 'You are about to completely delete the question type \'$a\'. Are you sure you want to uninstall it?';
$string['deleteunconfirmed'] = 'Delete not fully setup users after';
$string['deleteuser'] = 'Delete user';
+$string['deletingfilter'] = 'Deleting filter \'$a\'';
$string['deletingqtype'] = 'Deleting question type \'$a\'';
$string['density'] = 'Density';
$string['denyemailaddresses'] = 'Denied email domains';
@@ -417,6 +422,7 @@ $string['filestoredinhelp'] = 'Where the file will be stored';
$string['filterall'] = 'Filter all strings';
$string['filtermatchoneperpage'] = 'Filter match once per page';
$string['filtermatchonepertext'] = 'Filter match once per text';
+$string['filters'] = 'Filters';
$string['filtersettings'] = 'Manage filters';
$string['filtersettingsgeneral'] = 'General filter settings';
$string['filteruploadedfiles'] = 'Filter uploaded files';
@@ -427,6 +433,7 @@ $string['framename'] = 'Frame name';
$string['frontpage'] = 'Front Page';
$string['frontpagebackup'] = 'Front Page backup';
$string['frontpagedefaultrole'] = 'Default frontpage role';
+$string['frontpagefilters'] = 'Front page filters';
$string['frontpageloggedin'] = 'Front page items when logged in';
$string['frontpagequestions'] = 'Front Page questions';
$string['frontpageoverrides'] = 'Front Page permission overrides';
diff --git a/lang/en_utf8/error.php b/lang/en_utf8/error.php
index 023f984..52f7d89 100644
--- a/lang/en_utf8/error.php
+++ b/lang/en_utf8/error.php
@@ -40,6 +40,7 @@ $string['cannotcreateuploaddir'] = 'Cannot create upload folder. The site admini
$string['cannotcreateuser'] = 'Error creating user record';
$string['cannotcreateorfindstructs'] = 'Error finding or creating section structures for this course';
$string['cannotcreatepopupwin'] = 'Undefined element - cannot create pop-up window';
+$string['cannotcustomisefiltersblockuser'] = 'You cannot customise filters settings in user or block contexts.';
$string['cannotcustomizelocallang'] = 'You do not have permission to customize the strings translation. This permission is controlled by the capability \"moodle/site:langeditlocal\". Set this capability to allow you to edit local language packages in case you want to modify translations for your site.';
$string['cannotdeletelangcache'] = 'Language cache cannot be deleted, please fix permissions in dataroot/cache/languages!';
$string['cannotdeletebackupids'] = 'Couldn\'t delete previous backup ids';
@@ -221,6 +222,7 @@ $string['fieldrequired'] = '\"$a\" is a required field';
$string['filenotfound'] = 'Sorry, the requested file could not be found';
$string['filenotreadable'] = 'File is not readable';
$string['filemismatch'] = 'Non-core file name mismatch. The file \"$a->current\" should be $a->file';
+$string['filterdoesnothavelocalconfig'] = 'The filter $a does not allow local configuration.';
$string['filternotinstalled'] = 'Filter $a is not currently installed';
$string['filternotactive'] = 'Filter $a is not currently active';
$string['forumblockingtoomanyposts'] = 'You have exceeded the posting threshold set for this forum';
@@ -347,6 +349,7 @@ $string['noguest'] = 'No guests here!';
$string['nologinas'] = 'You are not allowed to login as that user';
$string['noadmins'] = 'No administrators!';
$string['noexistingcategory'] = 'No existing category';
+$string['nofiltersenabled'] = 'No filters are enabled.';
$string['notlocalisederrormessage'] = '$a';
$string['nousers'] = 'No such user!';
$string['nonmeaningfulcontent'] = 'Non meaningful content';
diff --git a/lang/en_utf8/filters.php b/lang/en_utf8/filters.php
index 3608dd6..e7c91d9 100644
--- a/lang/en_utf8/filters.php
+++ b/lang/en_utf8/filters.php
@@ -6,8 +6,11 @@ $string['anycourse'] = 'any course';
$string['anyfield'] = 'any field';
$string['anyrole'] = 'any role';
$string['anyvalue'] = 'any value';
+$string['applyto'] = 'Apply to';
$string['categoryrole'] = 'Category role';
$string['tablenosave'] = 'Changes in table above are saved automatically.';
+$string['content'] = 'Content';
+$string['contentandheadings'] = 'Content and headings';
$string['contains'] = 'contains';
$string['courserole'] = 'Course role';
$string['courserolelabel'] = '$a->label is $a->rolename in $a->coursename from $a->categoryname';
@@ -15,10 +18,16 @@ $string['courserolelabelerror'] = '$a->label error: course $a->coursename does n
$string['datelabelisafter'] = '$a->label is after $a->after';
$string['datelabelisbefore'] = '$a->label is before $a->before';
$string['datelabelisbetween'] = '$a->label is between $a->after and $a->before';
+$string['defaultx'] = 'Default ($a)';
+$string['disabled'] = 'Disabled';
$string['doesnotcontain'] = 'doesn\'t contain';
$string['endswith'] = 'ends with';
+$string['filterallwarning'] = 'Applying filters to headings as well as content can greatly increase the load on your server. Please use that \'Apply to\' settings sparingly. The main use is with the multilang filter.';
+$string['filtersettingsforin'] = 'Filter settings for $a->filter in $a->context';
+$string['filtersettingsin'] = 'Filter settings in $a';
$string['firstaccess'] = 'First access';
$string['globalrolelabel'] = '$a->label is $a->value';
+$string['isactive'] = 'Active?';
$string['isanyvalue'] = 'is any value';
$string['isafter'] = 'is after';
$string['isbefore'] = 'is before';
@@ -28,6 +37,10 @@ $string['isequalto'] = 'is equal to';
$string['isnotequalto'] = 'isn\'t equal to';
$string['isnotdefined'] = 'isn\'t defined';
$string['newfilter'] = 'New filter';
+$string['nofiltersenabled'] = 'No filter plugins have been enabled on this site.';
+$string['off'] = 'Off';
+$string['offbutavailable'] = 'Off, but available';
+$string['on'] = 'On';
$string['profilelabel'] = '$a->label: $a->profile $a->operator $a->value';
$string['profilelabelnovalue'] = '$a->label: $a->profile $a->operator';
$string['removeall'] = 'Remove all filters';
diff --git a/lang/en_utf8/help/localfiltersettings.html b/lang/en_utf8/help/localfiltersettings.html
new file mode 100644
index 0000000..175a7f2
--- /dev/null
+++ b/lang/en_utf8/help/localfiltersettings.html
@@ -0,0 +1,8 @@
+
Filter settings
+
+
This page lets you turn filters on or off in a particular part of the site.
+This page only gives you access to those filters that the Administrator has
+enabled.
+
+
Some filters may also let you set local settings, in which case there will be
+a 'Settings' link next to their name.
\ No newline at end of file
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:
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/adminlib.php b/lib/adminlib.php
index f771db5..d3d90e8 100644
--- a/lib/adminlib.php
+++ b/lib/adminlib.php
@@ -3711,7 +3711,8 @@ class admin_setting_managefilters extends admin_setting {
}
public function write_setting($data) {
- // do not write any setting
+ // do not write any settings. Instead all our UI submits to admin/filters.php
+ // which makes and changes, then redirects back.
return '';
}
@@ -3734,121 +3735,131 @@ class admin_setting_managefilters extends admin_setting {
return false;
}
- public function output_html($data, $query='') {
+ protected function action_url($filterpath, $action) {
global $CFG;
+ return $CFG->wwwroot . '/' . $CFG->admin . '/filters.php?sesskey=' . sesskey() .
+ '&filterpath=' . urlencode($filterpath) . '&action=' . $action;
+ }
- $strname = get_string('name');
- $strhide = get_string('disable');
- $strshow = get_string('enable');
- $strhideshow = "$strhide/$strshow";
- $strsettings = get_string('settings');
- $strup = get_string('up');
- $strdown = get_string('down');
- $strupdown = "$strup/$strdown";
-
- // get a list of possible filters (and translate name if possible)
- // note filters can be in the dedicated filters area OR in their
- // associated modules
- $installedfilters = filter_get_all_installed();
- $filtersettings_new = array();
- foreach ($installedfilters as $path => $strfiltername) {
- $settingspath_new = $CFG->dirroot . '/' . $path . '/filtersettings.php';
- if (is_readable($settingspath_new)) {
- $filtersettings_new[] = $path;
+ protected function action_icon($url, $icon, $straction) {
+ global $CFG;
+ return '' .
+ ' ';
+ }
+
+ protected function get_table_row($filterinfo, $isfirstrow, $islastactive, $applytostrings) {
+ global $CFG;
+ $row = array();
+ $filter = $filterinfo->filter;
+
+ // Filter name
+ $row[] = $this->filternames[$filter];
+
+ // Disable/off/on
+ $row[] = popup_form($this->action_url($filter, 'setstate') . '&newstate=', $this->activechoices,
+ 'active' . basename($filter), $filterinfo->active, '', '', '', true, 'self', '', NULL, get_string('save'));
+
+ // Re-order
+ $updown = '';
+ $spacer = ' ';
+ if ($filterinfo->active != TEXTFILTER_DISABLED) {
+ if (!$isfirstrow) {
+ $updown .= $this->action_icon($this->action_url($filter, 'up'), 'up', $this->strup);
+ } else {
+ $updown .= $spacer;
+ }
+ if (!$islastactive) {
+ $updown .= $this->action_icon($this->action_url($filter, 'down'), 'down', $this->strdown);
+ } else {
+ $updown .= $spacer;
}
}
+ $row[] = $updown;
- // get all the currently selected filters
- if (!empty($CFG->textfilters)) {
- $oldactivefilters = explode(',', $CFG->textfilters);
- $oldactivefilters = array_unique($oldactivefilters);
- } else {
- $oldactivefilters = array();
- }
+ // Apply to strings.
+ $row[] = popup_form($this->action_url($filter, 'setapplyto') . '&stringstoo=', $this->applytochoices,
+ 'applyto' . basename($filter), $applytostrings, '', '', '', true, 'self', '', NULL, get_string('save'),
+ $filterinfo->active == TEXTFILTER_DISABLED);
- // take this opportunity to clean up filters
- $activefilters = array();
- foreach ($oldactivefilters as $oldactivefilter) {
- if (!empty($oldactivefilter) and array_key_exists($oldactivefilter, $installedfilters)) {
- $activefilters[] = $oldactivefilter;
- }
+ // Settings link, if required
+ $settings = '';
+ if (filter_has_global_settings($filter)) {
+ $settings = '' . $this->strsettings . '';
}
+ $row[] = $settings;
- // Get the list of all filters, and pull the active filters
- // to the top.
- $displayfilters = array();
- foreach ($activefilters as $activefilter) {
- $name = $installedfilters[$activefilter];
- $displayfilters[$activefilter] = $name;
- }
- foreach ($installedfilters as $key => $filter) {
- if (!array_key_exists($key, $displayfilters)) {
- $displayfilters[$key] = $filter;
- }
+ return $row;
+ }
+
+ public function output_html($data, $query='') {
+ global $CFG;
+
+ $this->activechoices = array(
+ TEXTFILTER_DISABLED => get_string('disabled', 'filters'),
+ TEXTFILTER_OFF => get_string('offbutavailable', 'filters'),
+ TEXTFILTER_ON => get_string('on', 'filters'),
+ );
+ $this->applytochoices = array(
+ 0 => get_string('content', 'filters'),
+ 1 => get_string('contentandheadings', 'filters'),
+ );
+ $this->strup = get_string('up');
+ $this->strdown = get_string('down');
+ $this->strsettings = get_string('settings');
+
+ $filters = filter_get_global_states();
+
+ // In case any new filters have been installed, but not put in the table yet.
+ $this->filternames = filter_get_all_installed();
+ $newfilters = $this->filternames;
+ foreach ($filters as $filter => $notused) {
+ unset($newfilters[$filter]);
}
+ $stringfilters = filter_get_string_filters();
$return = print_heading(get_string('actfilterhdr', 'filters'), '', 3, 'main', true);
$return .= print_box_start('generalbox filtersui', '', true);
$table = new object();
- $table->head = array($strname, $strhideshow, $strupdown, $strsettings);
- $table->align = array('left', 'center', 'center', 'center');
- $table->width = '90%';
+ $table->head = array(get_string('filter'), get_string('isactive', 'filters'),
+ get_string('order'), get_string('applyto', 'filters'), $this->strsettings);
+ $table->align = array('left', 'left', 'center', 'left', 'left');
+ $table->width = '100%';
$table->data = array();
- $filtersurl = "$CFG->wwwroot/$CFG->admin/filters.php?sesskey=".sesskey();
- $imgurl = "$CFG->pixpath/t";
-
- // iterate through filters adding to display table
- $updowncount = 1;
- $activefilterscount = count($activefilters);
- foreach ($displayfilters as $path => $name) {
- $upath = urlencode($path);
- // get hide/show link
- if (in_array($path, $activefilters)) {
- $hideshow = "";
- $hideshow .= "pixpath}/i/hide.gif\" class=\"icon\" alt=\"$strhide\" />";
- $hidden = false;
- $displayname = "$name";
- }
- else {
- $hideshow = "";
- $hideshow .= "pixpath}/i/show.gif\" class=\"icon\" alt=\"$strshow\" />";
- $hidden = true;
- $displayname = "$name";
- }
-
- // get up/down link (only if not hidden)
- $updown = '';
- if (!$hidden) {
- if ($updowncount>1) {
- $updown .= "";
- $updown .= " ";
- }
- else {
- $updown .= "pixpath/spacer.gif\" class=\"icon\" alt=\"\" /> ";
- }
- if ($updowncount<$activefilterscount) {
- $updown .= "";
- $updown .= "";
- }
- else {
- $updown .= "pixpath/spacer.gif\" class=\"icon\" alt=\"\" />";
- }
- ++$updowncount;
+ $lastactive = null;
+ foreach ($filters as $filter => $filterinfo) {
+ if ($filterinfo->active != TEXTFILTER_DISABLED) {
+ $lastactive = $filter;
}
+ }
- // settings link (if defined)
- $settings = '';
- if (in_array($path, $filtersettings_new)) {
- $settings = "$strsettings";
+ // iterate through filters adding to display table
+ $firstrow = true;
+ foreach ($filters as $filter => $filterinfo) {
+ $applytostrings = isset($stringfilters[$filter]) && $filterinfo->active != TEXTFILTER_DISABLED;
+ $row = $this->get_table_row($filterinfo, $firstrow, $filter == $lastactive, $applytostrings);
+ $table->data[] = $row;
+ if ($filterinfo->active == TEXTFILTER_DISABLED) {
+ $table->rowclass[] = 'dimmed_text';
+ } else {
+ $table->rowclass[] = '';
}
-
- // write data into the table object
- $table->data[] = array($displayname, $hideshow, $updown, $settings);
+ $firstrow = false;
+ }
+ foreach ($newfilters as $filter => $filtername) {
+ $filterinfo = new stdClass;
+ $filterinfo->filter = $filter;
+ $filterinfo->active = TEXTFILTER_DISABLED;
+ $row = $this->get_table_row($filterinfo, false, false, false);
+ $table->data[] = $row;
+ $table->rowclass[] = 'dimmed_text';
}
+
$return .= print_table($table, true);
- $return .= get_string('tablenosave', 'filters');
+ $return .= '
' . get_string('tablenosave', 'filters') . '
';
+ $return .= '
' . get_string('filterallwarning', 'filters') . '
';
$return .= print_box_end(true);
return highlight($query, $return);
}
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/lib/db/install.php b/lib/db/install.php
index d703cad..691bf0f 100644
--- a/lib/db/install.php
+++ b/lib/db/install.php
@@ -41,24 +41,25 @@ function xmldb_main_install() {
/// create default course category
$cat = get_course_category();
-
- $defaults = array('rolesactive' => '0', // marks fully set up system
- 'auth' => 'email',
- 'auth_pop3mailbox' => 'INBOX',
- 'enrol' => 'manual',
- 'enrol_plugins_enabled' => 'manual',
- 'style' => 'default',
- 'template' => 'default',
- 'theme' => 'standardwhite',
- 'filter_multilang_converted' => 1,
- 'siteidentifier' => random_string(32).$_SERVER['HTTP_HOST'],
- 'backup_version' => 2008111700,
- 'backup_release' => '2.0 dev',
- 'blocks_version' => 2007081300, // might be removed soon
- 'mnet_dispatcher_mode' => 'off',
- 'sessiontimeout' => 7200, // must be present during roles installation
-
- );
+ $defaults = array(
+ 'rolesactive' => '0', // marks fully set up system
+ 'auth' => 'email',
+ 'auth_pop3mailbox' => 'INBOX',
+ 'enrol' => 'manual',
+ 'enrol_plugins_enabled' => 'manual',
+ 'style' => 'default',
+ 'template' => 'default',
+ 'theme' => 'standardwhite',
+ 'filter_multilang_converted' => 1,
+ 'siteidentifier' => random_string(32).$_SERVER['HTTP_HOST'],
+ 'backup_version' => 2008111700,
+ 'backup_release' => '2.0 dev',
+ 'blocks_version' => 2007081300, // might be removed soon
+ 'mnet_dispatcher_mode' => 'off',
+ 'sessiontimeout' => 7200, // must be present during roles installation
+ 'stringfilters' => '', // These two are managed in a strange way by the filters
+ 'filterall' => 0, // setting page, so have to be initialised here.
+ );
foreach($defaults as $key => $value) {
set_config($key, $value);
}
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..a9bd56d 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,103 @@ 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);
+ }
+
+ 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;
+ }
+
+ /// Main savepoint reached
+ upgrade_main_savepoint($result, 2009040302);
+ }
+
+ if ($result && $oldversion < 2009040600) {
+ /// Ensure that $CFG->stringfilters is set.
+ if (empty($CFG->stringfilters)) {
+ if (!empty($CFG->filterall)) {
+ set_config('stringfilters', $CFG->textfilters);
+ } else {
+ set_config('stringfilters', '');
+ }
+ }
+
+ set_config('filterall', !empty($CFG->stringfilters));
+ unset_config('textfilters');
+
+ /// Main savepoint reached
+ upgrade_main_savepoint($result, 2009040600);
+ }
+
return $result;
}
diff --git a/lib/filelib.php b/lib/filelib.php
index 4050407..0d83c78 100644
--- a/lib/filelib.php
+++ b/lib/filelib.php
@@ -1529,12 +1529,10 @@ function file_modify_html_header($text) {
$stylesheetshtml .= ''."\n";
}
- $filters = explode(",", $CFG->textfilters);
- if (in_array('filter/mediaplugin', $filters)) {
+ $ufo = '';
+ if (filter_is_enabled('filter/mediaplugin')) {
// this script is needed by most media filter plugins.
$ufo = get_require_js_code(array($CFG->wwwroot . '/lib/ufo.js'));
- } else {
- $ufo = '';
}
preg_match('/\|\/', $text, $matches);
diff --git a/lib/filterlib.php b/lib/filterlib.php
index 74f203f..c9de713 100644
--- a/lib/filterlib.php
+++ b/lib/filterlib.php
@@ -1,56 +1,287 @@
courseid = $courseid;
- $this->format = $format;
- $this->options = $options;
+/**
+ * Library functions for managing text filter plugins.
+ *
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ * @package moodlecore
+ */
+
+/**
+ * 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);
+
+/**
+ * Class to manage the filtering of strings. It is intended that this class is
+ * only used by weblib.php. Client code should probably be using the
+ * format_text and format_string functions.
+ *
+ * This class is a singleton.
+ */
+class filter_manager {
+ /** This list of active filters, by context, for filtering content.
+ * An array contextid => array of filter objects. */
+ protected $textfilters = array();
+
+ /** This list of active filters, by context, for filtering strings.
+ * An array contextid => array of filter objects. */
+ protected $stringfilters = array();
+
+ /** Exploded version of $CFG->stringfilters. */
+ protected $stringfilternames = array();
+
+ /** Holds the singleton instance. */
+ protected static $singletoninstance;
+
+ protected function __construct() {
+ $this->stringfilternames = filter_get_string_filters();
}
- public static function addfilter($classname, $obj) {
- if (empty(self::$filters[$classname])) {
- self::$filters[$classname] = $obj;
- return true;
- } else {
- return false;
+ /**
+ * @return the singleton instance.
+ */
+ public static function instance() {
+ if (is_null(self::$singletoninstance)) {
+ global $CFG;
+ if (!empty($CFG->perfdebug)) {
+ self::$singletoninstance = new performance_measuring_filter_manager();
+ } else {
+ self::$singletoninstance = new self();
+ }
}
+ return self::$singletoninstance;
}
- public static function do_filter($text, $courseid = null) {
+ /** Load all the filters required by this context. */
+ protected function load_filters($context, $courseid) {
+ $filters = filter_get_active_in_context($context);
+ $this->textfilters[$context->id] = array();
+ $this->stringfilters[$context->id] = array();
+ foreach ($filters as $filtername => $localconfig) {
+ $filter = $this->make_filter_object($filtername, $context, $courseid, $localconfig);
+ if (is_null($filter)) {
+ continue;
+ }
+ $this->textfilters[$context->id][] = $filter;
+ if (in_array($filtername, $this->stringfilternames)) {
+ $this->stringfilters[$context->id][] = $filter;
+ }
+ }
+ }
+
+ /**
+ * Factory method for creating a filter.
+ * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'.
+ * @param $context context object.
+ * @param $courseid course if.
+ * @param $localconfig array of local configuration variables for this filter.
+ * @return moodle_text_filter The filter, or null, if this type of filter is
+ * not recognised or could not be created.
+ */
+ protected function make_filter_object($filtername, $context, $courseid, $localconfig) {
global $CFG;
+ $path = $CFG->dirroot .'/'. $filtername .'/filter.php';
+ if (!is_readable($path)) {
+ return null;
+ }
+ include_once($path);
- foreach (self::$filters as $n=>$obj) {
- $text = $obj->filter($text);
+ $filterclassname = basename($filtername) . '_filter';
+ if (class_exists($filterclassname)) {
+ return new $filterclassname($courseid, $context, $localconfig);
}
- // back compatable with old filter plugins
- if (isset($CFG->textfilters)) {
- $textfilters = explode(',', $CFG->textfilters);
- foreach ($textfilters as $v) {
- $text_filter = basename($v).'_filter';
- if (empty(self::$filters[$text_filter]) && is_readable($CFG->dirroot .'/'. $v .'/filter.php')) {
- include_once($CFG->dirroot .'/'. $v .'/filter.php');
- if (function_exists($text_filter)) {
- $text = $text_filter($courseid, $text);
- }
- }
- }
+ $legacyfunctionname = basename($filtername) . '_filter';
+ if (function_exists($legacyfunctionname)) {
+ return new legacy_filter($legacyfunctionname, $courseid, $context, $localconfig);
+ }
+
+ return null;
+ }
+
+ protected function apply_filter_chain($text, $filterchain) {
+ foreach ($filterchain as $filter) {
+ $text = $filter->filter($text);
+ }
+ return $text;
+ }
+
+ protected function get_text_filters($context, $courseid) {
+ if (!isset($this->textfilters[$context->id])) {
+ $this->load_filters($context, $courseid);
+ }
+ return $this->textfilters[$context->id];
+ }
+
+ protected function get_string_filters($context, $courseid) {
+ if (!isset($this->stringfilters[$context->id])) {
+ $this->load_filters($context, $courseid);
}
+ return $this->stringfilters[$context->id];
+ }
+
+ public function filter_text($text, $context, $courseid) {
+ $text = $this->apply_filter_chain($text, $this->get_text_filters($context, $courseid));
+ /// tags removed for XHTML compatibility
+ $text = str_replace(array('', ''), '', $text);
+ return $text;
+ }
+
+ public function filter_string($string, $context, $courseid) {
+ return $this->apply_filter_chain($string, $this->get_string_filters($context, $courseid));
+ }
+
+ public function text_filtering_hash($context, $courseid) {
+ $filters = $this->get_text_filters($context, $courseid);
+ $hashes = array();
+ foreach ($filters as $filter) {
+ $hashes[] = $filter->hash();
+ }
+ return implode('-', $hashes);
+ }
+}
+
+/**
+ * Filter manager subclass that does nothing. Having this simplifies the logic
+ * of format_text, etc.
+ */
+class null_filter_manager {
+ public function filter_text($text, $context, $courseid) {
+ return $text;
+ }
+
+ public function filter_string($string, $context, $courseid) {
return $text;
}
+ public function text_filtering_hash() {
+ return '';
+ }
+}
+
+/**
+ * Filter manager subclass that tacks how much work it does.
+ */
+class performance_measuring_filter_manager extends filter_manager {
+ protected $filterscreated = 0;
+ protected $textsfiltered = 0;
+ protected $stringsfiltered = 0;
+
+ protected function make_filter_object($filtername, $context, $courseid, $localconfig) {
+ $this->filterscreated++;
+ return parent::make_filter_object($filtername, $context, $courseid, $localconfig);
+ }
+
+ public function filter_text($text, $context, $courseid) {
+ $this->textsfiltered++;
+ return parent::filter_text($text, $context, $courseid);
+ }
+
+ public function filter_string($string, $context, $courseid) {
+ $this->stringsfiltered++;
+ return parent::filter_string($string, $context, $courseid);
+ }
+
+ public function get_performance_summary() {
+ return array(array(
+ 'contextswithfilters' => count($this->textfilters),
+ 'filterscreated' => $this->filterscreated,
+ 'textsfiltered' => $this->textsfiltered,
+ 'stringsfiltered' => $this->stringsfiltered,
+ ), array(
+ 'contextswithfilters' => 'Contexts for which filters were loaded',
+ 'filterscreated' => 'Filters created',
+ 'textsfiltered' => 'Pieces of content filtered',
+ 'stringsfiltered' => 'Strings filtered',
+ ));
+ }
+}
+
+/**
+ * Base class for text filters. You just need to override this class and
+ * implement the filter method.
+ */
+abstract class moodle_text_filter {
+ /** The course we are in. */
+ protected $courseid;
+ /** The context we are in. */
+ protected $context;
+ /** Any local configuration for this filter in this context. */
+ protected $localconfig;
+
+ /**
+ * Set any context-specific configuration for this filter.
+ * @param object $context The current course id.
+ * @param object $context The current context.
+ * @param array $config Any context-specific configuration for this filter.
+ */
+ public function __construct($courseid, $context, array $localconfig) {
+ $this->courseid = $courseid;
+ $this->context = $context;
+ $this->localconfig = $localconfig;
+ }
+
public function hash() {
return __CLASS__;
}
- // filter plugin must overwrite this function to filter
- abstract function filter($text);
+ /**
+ * Override this funciton to actually implement the filtering.
+ * @param $text some HTML content.
+ * @return the HTML content after the filtering has been applied.
+ */
+ public abstract function filter($text);
+}
+
+/**
+ * moodle_text_filter implementation that encapsulates an old-style filter that
+ * only defines a function, not a class.
+ */
+class legacy_filter extends moodle_text_filter {
+ protected $filterfunction;
+
+ /**
+ * Set any context-specific configuration for this filter.
+ * @param string $filterfunction
+ * @param object $context The current course id.
+ * @param object $context The current context.
+ * @param array $config Any context-specific configuration for this filter.
+ */
+ public function __construct($filterfunction, $courseid, $context, array $localconfig) {
+ parent::__construct($courseid, $context, $localconfig);
+ $this->filterfunction = $filterfunction;
+ }
+
+ public function filter($text) {
+ return call_user_func($this->filterfunction, $this->courseid, $text);
+ }
}
/// Define one exclusive separator that we'll use in the temp saved tags
@@ -106,8 +337,9 @@ class filterobject {
* @param string $filter the folder name where the filter lives.
* @return string the human-readable name for this filter.
*/
-function filter_get_name($filterlocation, $filter) {
- switch ($filterlocation) {
+function filter_get_name($filter) {
+ list($type, $filter) = explode('/', $filter);
+ switch ($type) {
case 'filter':
$strfiltername = get_string('filtername', 'filter_' . $filter);
if (substr($strfiltername, 0, 2) != '[[') {
@@ -119,12 +351,12 @@ function filter_get_name($filterlocation, $filter) {
case 'mod':
$strfiltername = get_string('filtername', $filter);
if (substr($strfiltername, 0, 2) == '[[') {
- $strfiltername .= ' (' . $filterlocation . '/' . $filter . ')';
+ $strfiltername .= ' (' . $type . '/' . $filter . ')';
}
return $strfiltername;
default:
- throw new coding_exception('Unknown filter location ' . $filterlocation);
+ throw new coding_exception('Unknown filter type ' . $type);
}
}
@@ -143,7 +375,7 @@ function filter_get_all_installed() {
foreach ($filters as $filter) {
$path = $filterlocation . '/' . $filter;
if (is_readable($CFG->dirroot . '/' . $path . '/filter.php')) {
- $strfiltername = filter_get_name($filterlocation, $filter);
+ $strfiltername = filter_get_name($path);
$filternames[$path] = $strfiltername;
}
}
@@ -153,6 +385,411 @@ 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 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 when adding an unknown filter
+ * in state TEXTFILTER_ON or TEXTFILTER_OFF, or enabling/diabling an exsisting filter.
+ * Just after the very last filter when adding an unknown filter in state TEXTFILTER_DISABLED
+ */
+function filter_set_global_state($filter, $state, $sortorder = false) {
+ global $DB;
+
+ // 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.");
+ }
+
+ // Check sortorder is valid.
+ if ($sortorder !== false) {
+ if ($sortorder < 1 || $sortorder > $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 && $insert) {
+ $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);
+ }
+}
+
+/**
+ * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'.
+ * @return boolean is this filter allowed to be used on this site. That is, the
+ * admin has set the global 'active' setting to On, or Off, but available.
+ */
+function filter_is_enabled($filter) {
+ return array_key_exists($filter, filter_get_globally_enabled());
+}
+
+/**
+ * Return a list of all the filters that may be in use somewhere.
+ * @return array where the keys and values are both the filter name, like 'filter/tex'.
+ */
+function filter_get_globally_enabled() {
+ static $enabledfilters = null;
+ if (is_null($enabledfilters)) {
+ $filters = filter_get_global_states();
+ $enabledfilters = array();
+ foreach ($filters as $filter => $filerinfo) {
+ if ($filerinfo->active != TEXTFILTER_DISABLED) {
+ $enabledfilters[$filter] = $filter;
+ }
+ }
+ }
+ return $enabledfilters;
+}
+
+/**
+ * Return the names of the filters that should also be applied to strings
+ * (when they are enabled).
+ * @return array where the keys and values are both the filter name, like 'filter/tex'.
+ */
+function filter_get_string_filters() {
+ global $CFG;
+ $stringfilters = array();
+ if (!empty($CFG->filterall) && !empty($CFG->stringfilters)) {
+ $stringfilters = explode(',', $CFG->stringfilters);
+ $stringfilters = array_combine($stringfilters, $stringfilters);
+ }
+ return $stringfilters;
+}
+
+/**
+ * Sets whether a particular active filter should be applied to all strings by
+ * format_string, or just used by format_text.
+ * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'.
+ * @param boolean $applytostrings if true, this filter will apply to format_string
+ * and format_text, when it is enabled.
+ */
+function filter_set_applies_to_strings($filter, $applytostrings) {
+ $stringfilters = filter_get_string_filters();
+ $numstringfilters = count($stringfilters);
+ if ($applytostrings) {
+ $stringfilters[$filter] = $filter;
+ } else {
+ unset($stringfilters[$filter]);
+ }
+ if (count($stringfilters) != $numstringfilters) {
+ set_config('stringfilters', implode(',', $stringfilters));
+ set_config('filterall', !empty($stringfilters));
+ }
+}
+
+/**
+ * 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 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);
+ }
+}
+
+/**
+ * Remove 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.
+ */
+function filter_unset_local_config($filter, $contextid, $name) {
+ global $DB;
+ $DB->delete_records('filter_config', array('filter' => $filter, 'contextid' => $contextid, 'name' => $name));
+}
+
+/**
+ * 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');
+}
+
+/**
+ * This function is for use by backup. Gets all the filter information specific
+ * to one context.
+ * @return array with two elements. The first element is an array of objects with
+ * fields filter and active. These come from the filter_active table. The
+ * second element is an array of objects with fields filter, name and value
+ * from the filter_config table.
+ */
+function filter_get_all_local_settings($contextid) {
+ global $DB;
+ $context = get_context_instance(CONTEXT_SYSTEM);
+ return array(
+ $DB->get_records('filter_active', array('contextid' => $contextid), 'filter', 'filter,active'),
+ $DB->get_records('filter_config', array('contextid' => $contextid), 'filter,name', 'filter,name,value'),
+ );
+}
+
+/**
+ * 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 filter_get_active_in_context($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 AND fc.contextid = $context->id");
+
+ // 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;
+}
+
+/**
+ * List all of the filters that are available in this context, and what the
+ * local and interited states of that filter are.
+ * @param object $context a context that is not the system context.
+ * @return array an array with filter names, for example 'filter/tex' or
+ * 'mod/glossary' as keys. and and the values are objects with fields:
+ * ->filter filter name, same as the key.
+ * ->localstate TEXTFILTER_ON/OFF/INHERIT
+ * ->inheritedstate TEXTFILTER_ON/OFF - the state that will be used if localstate is set to TEXTFILTER_INHERIT.
+ */
+function filter_get_available_in_context($context) {
+ global $DB;
+
+ // The complex logic is working out the active state in the parent context,
+ // so strip the current context from the list.
+ $contextids = explode('/', trim($context->path, '/'));
+ array_pop($contextids);
+ $contextids = implode(',', $contextids);
+ if (empty($contextids)) {
+ throw new coding_exception('filter_get_available_in_context cannot be called with the system context.');
+ }
+
+ // The following SQL is tricky, in the same way at the SQL in filter_get_active_in_context.
+ return $DB->get_records_sql(
+ "SELECT parent_states.filter,
+ CASE WHEN fa.active IS NULL THEN " . TEXTFILTER_INHERIT . "
+ ELSE fa.active END AS localstate,
+ parent_states.inheritedstate
+ FROM (SELECT f.filter,
+ CASE WHEN MAX(f.active * ctx.depth) > -MIN(f.active * ctx.depth) THEN " . TEXTFILTER_ON . "
+ ELSE " . TEXTFILTER_OFF . " END AS inheritedstate
+ FROM {filter_active} f
+ JOIN {context} ctx ON f.contextid = ctx.id
+ WHERE ctx.id IN ($contextids)
+ GROUP BY filter
+ HAVING MIN(f.active) > " . TEXTFILTER_DISABLED . "
+ ORDER BY MAX(f.sortorder)) parent_states
+ LEFT JOIN {filter_active} fa ON fa.filter = parent_states.filter AND fa.contextid = $context->id");
+}
+
+/**
+ * This function is for use by the filter administration page.
+ * @return array 'filtername' => object with fields 'filter' (=filtername), 'active' and 'sortorder'
+ */
+function filter_get_global_states() {
+ global $DB;
+ $context = get_context_instance(CONTEXT_SYSTEM);
+ return $DB->get_records('filter_active', array('contextid' => $context->id), 'sortorder', 'filter,active,sortorder');
+}
+
+/**
+ * Delete all the data in the database relating to a filter, prior to deleting it.
+ * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'.
+ */
+function filter_delete_all_data($filter) {
+ global $DB;
+ if (substr($filter, 0, 7) == 'filter/') {
+ unset_all_config_for_plugin('filter_' . basename($filter));
+ }
+ $DB->delete_records('filter_active', array('filter' => $filter));
+ $DB->delete_records('filter_config', array('filter' => $filter));
+}
+
+/**
+ * Does this filter have a global settings page in the admin tree?
+ * (The settings page for a filter must be called, for example,
+ * filtersettingfiltertex or filtersettingmodglossay.)
+ *
+ * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'.
+ * @return boolean Whether there should be a 'Settings' link on the config page.
+ */
+function filter_has_global_settings($filter) {
+ global $CFG;
+ $settingspath = $CFG->dirroot . '/' . $filter . '/filtersettings.php';
+ return is_readable($settingspath);
+}
+
+/**
+ * Does this filter have local (per-context) settings?
+ *
+ * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'.
+ * @return boolean Whether there should be a 'Settings' link on the manage filters in context page.
+ */
+function filter_has_local_settings($filter) {
+ global $CFG;
+ $settingspath = $CFG->dirroot . '/' . $filter . '/filterlocalsettings.php';
+ return is_readable($settingspath);
+}
+
+/**
+ * Certain types of context (block and user) may not have local filter settings.
+ * the function checks a context to see whether it may have local config.
+ * @param object $context a context.
+ * @return boolean whether this context may have local filter settings.
+ */
+function filter_context_may_have_filter_settings($context) {
+ return $context->contextlevel != CONTEXT_BLOCK && $context->contextlevel != CONTEXT_USER;
+}
+
+/**
* Process phrases intelligently found within a HTML text (such as adding links)
*
* param text the text that we are filtering
diff --git a/lib/moodlelib.php b/lib/moodlelib.php
index bd3cb80..e46cda2 100644
--- a/lib/moodlelib.php
+++ b/lib/moodlelib.php
@@ -7967,6 +7967,16 @@ function get_performance_info() {
$info['html'] .= 'Included '.$info['includecount'].' files ';
$info['txt'] .= 'includecount: '.$info['includecount'].' ';
+ $filtermanager = filter_manager::instance();
+ if (method_exists($filtermanager, 'get_performance_summary')) {
+ list($filterinfo, $nicenames) = $filtermanager->get_performance_summary();
+ $info = array_merge($filterinfo, $info);
+ foreach ($filterinfo as $key => $value) {
+ $info['html'] .= "$nicenames[$key]: $value ";
+ $info['txt'] .= "$key: $value ";
+ }
+ }
+
if (!empty($PERF->logwrites)) {
$info['logwrites'] = $PERF->logwrites;
$info['html'] .= 'Log DB writes '.$info['logwrites'].' ';
@@ -8362,7 +8372,7 @@ function get_plugin_name($plugin, $type='mod') {
}
break;
case 'filter':
- $plugin_name = filter_get_name('filter', $plugin);
+ $plugin_name = filter_get_name('filter/' . $plugin);
break;
default:
$plugin_name = $plugin;
diff --git a/lib/simpletest/filtersettingsperformancetester.php b/lib/simpletest/filtersettingsperformancetester.php
new file mode 100644
index 0000000..6ccad54
--- /dev/null
+++ b/lib/simpletest/filtersettingsperformancetester.php
@@ -0,0 +1,237 @@
+libdir . '/ddllib.php');
+
+require_login();
+$syscontext = get_context_instance(CONTEXT_SYSTEM);
+require_capability('moodle/site:config', $syscontext);
+
+$baseurl = $CFG->wwwroot . '/lib/simpletest/filtersettingsperformancetester.php';
+
+$title = 'filter_get_active_in_context performance test';
+print_header($title, $title, build_navigation($title));
+
+// Complain if we get this far and $CFG->unittestprefix is not set.
+if (empty($CFG->unittestprefix)) {
+ throw new coding_exception('This page requires $CFG->unittestprefix to be set in config.php.');
+}
+
+$requiredtables = array('context', 'filter_active', 'filter_config');
+$realdb = $DB;
+$testdb = moodle_database::get_driver_instance($CFG->dbtype, $CFG->dblibrary);
+$testdb->connect($CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->unittestprefix);
+$DB = $testdb;
+$dbman = $testdb->get_manager();
+$issetup = 0;
+foreach ($requiredtables as $table) {
+ if ($dbman->table_exists(new xmldb_table($table))) {
+ $issetup++;
+ }
+}
+
+switch (optional_param('action', '', PARAM_ACTION)) {
+ case 'setup':
+ if ($issetup == 0) {
+ foreach ($requiredtables as $table) {
+ $dbman->install_one_table_from_xmldb_file($CFG->dirroot . '/lib/db/install.xml', $table);
+ $issetup++;
+ }
+ flush();
+ populate_test_database($syscontext, 10, 100, 1000, 5000, 5000);
+ notify('Test tables created.', 'notifysuccess');
+ } else if ($issetup == count($requiredtables)) {
+ notify('Test tables are already set up.', 'notifysuccess');
+ } else {
+ notify('Something is wrong, please delete the test tables and try again.');
+ }
+ break;
+
+ case 'teardown':
+ foreach ($requiredtables as $tablename) {
+ $table = new xmldb_table($tablename);
+ if ($dbman->table_exists($table)) {
+ $dbman->drop_table($table);
+ }
+ }
+ $issetup = 0;
+ notify('Test tables dropped.', 'notifysuccess');
+ break;
+
+ case 'test':
+ if ($issetup != count($requiredtables)) {
+ notify('Something is wrong, please delete the test tables and try again.');
+ } else {
+ $contexts = $DB->get_records('context');
+ $numcalls = 1000;
+ $basetime = run_tests('noop', $contexts, $numcalls, 0);
+ run_tests('simple_get_record_by_id', $contexts, $numcalls, $basetime);
+ run_tests('filter_get_active_in_context', $contexts, $numcalls, $basetime);
+ }
+ break;
+}
+
+if ($issetup == count($requiredtables)) {
+ echo '
Total of ' . $DB->count_records('context') . ' contexts, ' .
+ $DB->count_records('filter_active') . ' filter_active and ' .
+ $DB->count_records('filter_config') . ' filter_config rows in the database.