From 69948eda103573ceb38fc54f0d34f500460e6e2b Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Mon, 27 Mar 2023 10:30:26 +0800 Subject: [PATCH 01/11] MDL-77105 core: Method to determine whether a plugin has monolog icons --- lib/classes/component.php | 18 ++++++++++++++++++ lib/tests/component_test.php | 15 +++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/lib/classes/component.php b/lib/classes/component.php index fb6fc9012b0..65e63a88369 100644 --- a/lib/classes/component.php +++ b/lib/classes/component.php @@ -1282,4 +1282,22 @@ $cache = '.var_export($cache, true).'; } return $componentnames; } + + /** + * Checks for the presence of monologo icons within a plugin. + * + * Only checks monologo icons in PNG and SVG formats as they are + * formats that can have transparent background. + * + * @param string $plugintype The plugin type. + * @param string $pluginname The plugin name. + * @return bool True if the plugin has a monologo icon + */ + public static function has_monologo_icon(string $plugintype, string $pluginname): bool { + $plugindir = core_component::get_plugin_directory($plugintype, $pluginname); + if ($plugindir === null) { + return false; + } + return file_exists("$plugindir/pix/monologo.svg") || file_exists("$plugindir/pix/monologo.png"); + } } diff --git a/lib/tests/component_test.php b/lib/tests/component_test.php index 2d0cc23632c..03ffe440714 100644 --- a/lib/tests/component_test.php +++ b/lib/tests/component_test.php @@ -861,4 +861,19 @@ class component_test extends advanced_testcase { $this->assertContains('tool_usertours', $componentnames); $this->assertContains('core_favourites', $componentnames); } + + /** + * Test for monologo icons check in plugins. + * + * @covers core_component::has_monologo_icon + * @return void + */ + public function test_has_monologo_icon(): void { + // The Forum activity plugin has monologo icons. + $this->assertTrue(core_component::has_monologo_icon('mod', 'forum')); + // The core H5P subsystem doesn't have monologo icons. + $this->assertFalse(core_component::has_monologo_icon('core', 'h5p')); + // The function will return false for a non-existent component. + $this->assertFalse(core_component::has_monologo_icon('randomcomponent', 'h5p')); + } } From 30b0b0cf40b5f8c764e86c8c58f2986cfe0f4d79 Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Fri, 10 Feb 2023 16:17:34 +0800 Subject: [PATCH 02/11] MDL-77105 core: Add a filtericon parameter to course mod icon URLs * If a plugin defines a `filtericon` custom data or uses its monologo version of the icon, a `filtericon` parameter is being added to the icon's URL. This information can help plugins determine whether to render the activity icon as is or with CSS filtering. --- lib/modinfolib.php | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/modinfolib.php b/lib/modinfolib.php index d0eb3e29b8b..bb5b3bc3543 100644 --- a/lib/modinfolib.php +++ b/lib/modinfolib.php @@ -1763,7 +1763,15 @@ class cm_info implements IteratorAggregate { } /** - * @param moodle_core_renderer $output Output render to use, or null for default (global) + * Fetch the module's icon URL. + * + * This function fetches the course module instance's icon URL. + * This method adds a `filtericon` parameter in the URL when rendering the monologo version of the course module icon or when + * the plugin declares, via its `filtericon` custom data, that the icon needs to be filtered. + * This additional information can be used by plugins when rendering the module icon to determine whether to apply + * CSS filtering to the icon. + * + * @param core_renderer $output Output render to use, or null for default (global) * @return moodle_url Icon URL for a suitable icon to put beside this cm */ public function get_icon_url($output = null) { @@ -1773,6 +1781,7 @@ class cm_info implements IteratorAggregate { $output = $OUTPUT; } + $ismonologo = false; if (!empty($this->iconurl)) { // Support modules setting their own, external, icon image. $icon = $this->iconurl; @@ -1792,6 +1801,17 @@ class cm_info implements IteratorAggregate { } } else { $icon = $output->image_url('monologo', $this->modname); + // Activity modules may only have an `icon` icon instead of a `monologo` icon. + // So we need to determine if the module really has a `monologo` icon. + $ismonologo = core_component::has_monologo_icon('mod', $this->modname); + } + + // Determine whether the icon will be filtered in the CSS. + // This can be controlled by the module by declaring a 'filtericon' custom data. + // If the 'filtericon' custom data is not set, icon filtering will be determined whether the module has a `monologo` icon. + $filtericon = $this->customdata['filtericon'] ?? $ismonologo; + if ($filtericon) { + $icon->param('filtericon', 1); } return $icon; } From f5d445a68fd9235acb18057733ef1ede8df72867 Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Fri, 10 Feb 2023 16:19:54 +0800 Subject: [PATCH 03/11] MDL-77105 core: Conditionally apply icon filter * Apply the filter CSS property only to activity icons that don't have the ".nofilter" class. This will allow activities with non-SVG icons to be rendered as they are. --- theme/boost/scss/moodle/icons.scss | 4 +++- theme/boost/style/moodle.css | 24 ++++++++++++------------ theme/classic/style/moodle.css | 24 ++++++++++++------------ 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/theme/boost/scss/moodle/icons.scss b/theme/boost/scss/moodle/icons.scss index 17bf8fe0cae..fa6baf1999c 100644 --- a/theme/boost/scss/moodle/icons.scss +++ b/theme/boost/scss/moodle/icons.scss @@ -151,7 +151,9 @@ $iconsizes: map-merge(( background-color: $value; .activityicon, .icon { - filter: brightness(0) invert(1); + &:not(.nofilter) { + filter: brightness(0) invert(1); + } } } } diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css index b1b96fab2c3..e7e8fe341f7 100644 --- a/theme/boost/style/moodle.css +++ b/theme/boost/style/moodle.css @@ -12506,38 +12506,38 @@ blockquote { .activityiconcontainer.administration { background-color: #5d63f6; } - .activityiconcontainer.administration .activityicon, - .activityiconcontainer.administration .icon { + .activityiconcontainer.administration .activityicon:not(.nofilter), + .activityiconcontainer.administration .icon:not(.nofilter) { filter: brightness(0) invert(1); } .activityiconcontainer.assessment { background-color: #eb66a2; } - .activityiconcontainer.assessment .activityicon, - .activityiconcontainer.assessment .icon { + .activityiconcontainer.assessment .activityicon:not(.nofilter), + .activityiconcontainer.assessment .icon:not(.nofilter) { filter: brightness(0) invert(1); } .activityiconcontainer.collaboration { background-color: #f7634d; } - .activityiconcontainer.collaboration .activityicon, - .activityiconcontainer.collaboration .icon { + .activityiconcontainer.collaboration .activityicon:not(.nofilter), + .activityiconcontainer.collaboration .icon:not(.nofilter) { filter: brightness(0) invert(1); } .activityiconcontainer.communication { background-color: #11a676; } - .activityiconcontainer.communication .activityicon, - .activityiconcontainer.communication .icon { + .activityiconcontainer.communication .activityicon:not(.nofilter), + .activityiconcontainer.communication .icon:not(.nofilter) { filter: brightness(0) invert(1); } .activityiconcontainer.content { background-color: #399be2; } - .activityiconcontainer.content .activityicon, - .activityiconcontainer.content .icon { + .activityiconcontainer.content .activityicon:not(.nofilter), + .activityiconcontainer.content .icon:not(.nofilter) { filter: brightness(0) invert(1); } .activityiconcontainer.interface { background-color: #a378ff; } - .activityiconcontainer.interface .activityicon, - .activityiconcontainer.interface .icon { + .activityiconcontainer.interface .activityicon:not(.nofilter), + .activityiconcontainer.interface .icon:not(.nofilter) { filter: brightness(0) invert(1); } :root { diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css index 63c38fdc2e2..9cbf8056dba 100644 --- a/theme/classic/style/moodle.css +++ b/theme/classic/style/moodle.css @@ -12506,38 +12506,38 @@ blockquote { .activityiconcontainer.administration { background-color: #5d63f6; } - .activityiconcontainer.administration .activityicon, - .activityiconcontainer.administration .icon { + .activityiconcontainer.administration .activityicon:not(.nofilter), + .activityiconcontainer.administration .icon:not(.nofilter) { filter: brightness(0) invert(1); } .activityiconcontainer.assessment { background-color: #eb66a2; } - .activityiconcontainer.assessment .activityicon, - .activityiconcontainer.assessment .icon { + .activityiconcontainer.assessment .activityicon:not(.nofilter), + .activityiconcontainer.assessment .icon:not(.nofilter) { filter: brightness(0) invert(1); } .activityiconcontainer.collaboration { background-color: #f7634d; } - .activityiconcontainer.collaboration .activityicon, - .activityiconcontainer.collaboration .icon { + .activityiconcontainer.collaboration .activityicon:not(.nofilter), + .activityiconcontainer.collaboration .icon:not(.nofilter) { filter: brightness(0) invert(1); } .activityiconcontainer.communication { background-color: #11a676; } - .activityiconcontainer.communication .activityicon, - .activityiconcontainer.communication .icon { + .activityiconcontainer.communication .activityicon:not(.nofilter), + .activityiconcontainer.communication .icon:not(.nofilter) { filter: brightness(0) invert(1); } .activityiconcontainer.content { background-color: #399be2; } - .activityiconcontainer.content .activityicon, - .activityiconcontainer.content .icon { + .activityiconcontainer.content .activityicon:not(.nofilter), + .activityiconcontainer.content .icon:not(.nofilter) { filter: brightness(0) invert(1); } .activityiconcontainer.interface { background-color: #a378ff; } - .activityiconcontainer.interface .activityicon, - .activityiconcontainer.interface .icon { + .activityiconcontainer.interface .activityicon:not(.nofilter), + .activityiconcontainer.interface .icon:not(.nofilter) { filter: brightness(0) invert(1); } :root { From 84198e03c76ca3e46f58c3b447f4a3d9cd4c2b2d Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Fri, 10 Feb 2023 16:21:43 +0800 Subject: [PATCH 04/11] MDL-77105 course_format: Add 'nofilter' class when necessary Add the `.nofilter` class for activity icons when the icon URL's `filtericon` parameter is not set, so they get rendered as they are on the course homepage. --- course/format/classes/output/local/content/cm/cmname.php | 5 ++++- course/format/templates/local/content/cm/cmname.mustache | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/course/format/classes/output/local/content/cm/cmname.php b/course/format/classes/output/local/content/cm/cmname.php index 0aa979d2e9a..9c7c9ad3210 100644 --- a/course/format/classes/output/local/content/cm/cmname.php +++ b/course/format/classes/output/local/content/cm/cmname.php @@ -102,9 +102,12 @@ class cmname implements named_templatable, renderable { return []; } + $iconurl = $mod->get_icon_url(); + $iconclass = $iconurl->get_param('filtericon') ? '' : 'nofilter'; $data = [ 'url' => $mod->url, - 'icon' => $mod->get_icon_url(), + 'icon' => $iconurl, + 'iconclass' => $iconclass, 'modname' => $mod->modname, 'textclasses' => $displayoptions['textclasses'] ?? '', 'purpose' => plugin_supports('mod', $mod->modname, FEATURE_MOD_PURPOSE, MOD_PURPOSE_OTHER), diff --git a/course/format/templates/local/content/cm/cmname.mustache b/course/format/templates/local/content/cm/cmname.mustache index 5c364dbebb3..62389885190 100644 --- a/course/format/templates/local/content/cm/cmname.mustache +++ b/course/format/templates/local/content/cm/cmname.mustache @@ -25,6 +25,7 @@ { "url": "#", "icon": "../../../pix/help.svg", + "iconclass": "", "pluginname": "File", "textclasses": "", "purpose": "content", @@ -47,7 +48,7 @@
- {{{modname}}} icon + {{{modname}}} icon
{{#pluginname}} From f59220a76d0259f9b8547b575f89e14ae7ccf722 Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Fri, 10 Feb 2023 16:59:31 +0800 Subject: [PATCH 05/11] MDL-77105 theme_boost: Add 'nofilter' class when necessary Add the `.nofilter` class for activity icons when the icon URL's `filtericon` parameter is not set, so they get rendered as they are on the context header on the activity page. --- theme/boost/classes/output/core_renderer.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/theme/boost/classes/output/core_renderer.php b/theme/boost/classes/output/core_renderer.php index d5123d02749..7f6520b812d 100644 --- a/theme/boost/classes/output/core_renderer.php +++ b/theme/boost/classes/output/core_renderer.php @@ -161,8 +161,13 @@ class core_renderer extends \core_renderer { $heading = format_string($this->page->course->fullname, true, ['context' => $context]); } else { $heading = $this->page->cm->get_formatted_name(); - $imagedata = html_writer::img($this->page->cm->get_icon_url()->out(false), '', - ['class' => 'icon activityicon', 'aria-hidden' => 'true']); + $iconurl = $this->page->cm->get_icon_url(); + $iconclass = $iconurl->get_param('filtericon') ? '' : 'nofilter'; + $iconattrs = [ + 'class' => "icon activityicon $iconclass", + 'aria-hidden' => 'true' + ]; + $imagedata = html_writer::img($iconurl->out(false), '', $iconattrs); $purposeclass = plugin_supports('mod', $this->page->activityname, FEATURE_MOD_PURPOSE); $purposeclass .= ' activityiconcontainer'; $purposeclass .= ' modicon_' . $this->page->activityname; From 18703b71acaa4409305f584b91acfbafeb1149b9 Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Fri, 10 Feb 2023 18:42:47 +0800 Subject: [PATCH 06/11] MDL-77105 block_recentlyaccesseditems: Add 'nofilter' class Add the `.nofilter` class for activity icons when the icon URL's `filtericon` parameter is not set, so they get rendered as they are on the recently accessed items block. --- .../external/recentlyaccesseditems_item_exporter.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/blocks/recentlyaccesseditems/classes/external/recentlyaccesseditems_item_exporter.php b/blocks/recentlyaccesseditems/classes/external/recentlyaccesseditems_item_exporter.php index bff39633dbc..549a2ec9d50 100644 --- a/blocks/recentlyaccesseditems/classes/external/recentlyaccesseditems_item_exporter.php +++ b/blocks/recentlyaccesseditems/classes/external/recentlyaccesseditems_item_exporter.php @@ -51,17 +51,18 @@ class recentlyaccesseditems_item_exporter extends \core\external\exporter { protected function get_other_values(renderer_base $output) { global $CFG; require_once($CFG->libdir.'/modinfolib.php'); - + $iconurl = get_fast_modinfo($this->data->courseid)->cms[$this->data->cmid]->get_icon_url(); + $iconclass = $iconurl->get_param('filtericon') ? '' : 'nofilter'; return array( 'viewurl' => (new moodle_url('/mod/'.$this->data->modname.'/view.php', array('id' => $this->data->cmid)))->out(false), 'courseviewurl' => (new moodle_url('/course/view.php', array('id' => $this->data->courseid)))->out(false), 'icon' => \html_writer::img( - get_fast_modinfo($this->data->courseid)->cms[$this->data->cmid]->get_icon_url(), + $iconurl, get_string('pluginname', $this->data->modname), - ['title' => get_string('pluginname', $this->data->modname), 'class' => 'icon'] + ['title' => get_string('pluginname', $this->data->modname), 'class' => "icon $iconclass"] ), - 'purpose' => plugin_supports('mod', $this->data->modname, FEATURE_MOD_PURPOSE, MOD_PURPOSE_OTHER) + 'purpose' => plugin_supports('mod', $this->data->modname, FEATURE_MOD_PURPOSE, MOD_PURPOSE_OTHER), ); } From 28d8c9c1e73d28d8ccc33738e7cd2743054f759c Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Fri, 10 Feb 2023 18:43:25 +0800 Subject: [PATCH 07/11] MDL-77105 block_timeline: Add 'nofilter' class when necessary Add the `.nofilter` class for activity icons when the icon URL's `filtericon` parameter is not set, so they get rendered as they are on the timeline block. --- blocks/timeline/templates/event-list-item.mustache | 2 +- calendar/classes/external/event_icon_exporter.php | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/blocks/timeline/templates/event-list-item.mustache b/blocks/timeline/templates/event-list-item.mustache index 7c442c13075..13f28089897 100644 --- a/blocks/timeline/templates/event-list-item.mustache +++ b/blocks/timeline/templates/event-list-item.mustache @@ -57,7 +57,7 @@
{{#icon}} {{#iconurl}} - {{alttext}} + {{alttext}} {{/iconurl}} {{^iconurl}} {{#pix}} {{key}}, {{component}}, {{alttext}} {{/pix}} diff --git a/calendar/classes/external/event_icon_exporter.php b/calendar/classes/external/event_icon_exporter.php index f6bdf4c12f0..bdc8b6386d3 100644 --- a/calendar/classes/external/event_icon_exporter.php +++ b/calendar/classes/external/event_icon_exporter.php @@ -62,12 +62,15 @@ class event_icon_exporter extends exporter { $isgroupevent = ($group && !empty($groupid)); $isuserevent = ($user && !empty($userid)); $iconurl = ''; + $iconclass = ''; if ($isactivityevent) { $key = 'monologo'; $component = $coursemodule->get('modname'); - $iconurl = get_fast_modinfo($courseid)->get_cm($coursemodule->get('id'))->get_icon_url()->out(false); + $iconurl = get_fast_modinfo($courseid)->get_cm($coursemodule->get('id'))->get_icon_url(); + $iconclass = $iconurl->get_param('filtericon') ? '' : 'nofilter'; + $iconurl = $iconurl->out(false); if (get_string_manager()->string_exists($event->get_type(), $component)) { $alttext = get_string($event->get_type(), $component); } else { @@ -121,6 +124,7 @@ class event_icon_exporter extends exporter { $data->component = $component; $data->alttext = $alttext; $data->iconurl = $iconurl; + $data->iconclass = $iconclass; parent::__construct($data, $related); } @@ -136,6 +140,7 @@ class event_icon_exporter extends exporter { 'component' => ['type' => PARAM_TEXT], 'alttext' => ['type' => PARAM_TEXT], 'iconurl' => ['type' => PARAM_TEXT], + 'iconclass' => ['type' => PARAM_TEXT], ]; } From 538e17e199ce7c96ae421f2e29100f7667ebebe3 Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Fri, 10 Feb 2023 22:33:37 +0800 Subject: [PATCH 08/11] MDL-77105 core_course: Add 'nofilter' class for non-monologo icons When rendering content items, check whether the plugin has monologo icons. If so, add a 'nofilter' class so the plugin icon can be rendered as is and without the CSS filter. --- .../repository/content_item_readonly_repository.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/course/classes/local/repository/content_item_readonly_repository.php b/course/classes/local/repository/content_item_readonly_repository.php index 32bd29d2880..ae4c64861fb 100644 --- a/course/classes/local/repository/content_item_readonly_repository.php +++ b/course/classes/local/repository/content_item_readonly_repository.php @@ -26,6 +26,7 @@ namespace core_course\local\repository; defined('MOODLE_INTERNAL') || die(); +use core_component; use core_course\local\entity\content_item; use core_course\local\entity\lang_string_title; use core_course\local\entity\string_title; @@ -195,12 +196,20 @@ class content_item_readonly_repository implements content_item_readonly_reposito $archetype = plugin_supports('mod', $mod->name, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER); $purpose = plugin_supports('mod', $mod->name, FEATURE_MOD_PURPOSE, MOD_PURPOSE_OTHER); + $icon = 'monologo'; + // Quick check for monologo icons. + // Plugins that don't have monologo icons will be displayed as is and CSS filter will not be applied. + $hasmonologoicons = core_component::has_monologo_icon('mod', $mod->name); + $iconclass = ''; + if (!$hasmonologoicons) { + $iconclass = 'nofilter'; + } $contentitem = new content_item( $mod->id, $mod->name, new lang_string_title("modulename", $mod->name), new \moodle_url('/course/mod.php', ['id' => $course->id, 'add' => $mod->name]), - $OUTPUT->pix_icon('monologo', '', $mod->name, ['class' => 'icon activityicon']), + $OUTPUT->pix_icon($icon, '', $mod->name, ['class' => "activityicon $iconclass"]), $help, $archetype, 'mod_' . $mod->name, From 67f739499b3840ffc4cfae5a5a04c8dcd33bf3ab Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Fri, 10 Feb 2023 19:06:59 +0800 Subject: [PATCH 09/11] MDL-77105 mod_lti: Add 'nofilter' class for custom tool icons Add a '.nofilter' class when rendering custom tool icons in order to render them as is and without CSS filter on the activity chooser. --- mod/lti/locallib.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/mod/lti/locallib.php b/mod/lti/locallib.php index 1ced06aeea0..8c1728fc95b 100644 --- a/mod/lti/locallib.php +++ b/mod/lti/locallib.php @@ -2387,7 +2387,15 @@ function lti_get_configured_types($courseid, $sectionreturn = 0) { $type->help = clean_param($trimmeddescription, PARAM_NOTAGS); $type->helplink = get_string('modulename_shortcut_link', 'lti'); } - $type->icon = html_writer::empty_tag('img', ['src' => get_tool_type_icon_url($ltitype), 'alt' => '', 'class' => 'icon']); + + $iconurl = get_tool_type_icon_url($ltitype); + $iconclass = ''; + if ($iconurl !== $OUTPUT->image_url('monologo', 'lti')->out()) { + // Do not filter the icon if it is not the default LTI activity icon. + $iconclass = 'nofilter'; + } + $type->icon = html_writer::empty_tag('img', ['src' => $iconurl, 'alt' => '', 'class' => "icon $iconclass"]); + $type->link = new moodle_url('/course/modedit.php', array('add' => 'lti', 'return' => 0, 'course' => $courseid, 'sr' => $sectionreturn, 'typeid' => $ltitype->id)); $types[] = $type; From 99bc8f2b309b3dca1c339b22fd98fde68c99ba6b Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Fri, 10 Feb 2023 22:34:45 +0800 Subject: [PATCH 10/11] MDL-77105 mod_url: Declare filtericon custom data * Set a custom data `filtericon` when the icon being rendered for the URL resource is not equal to the default plugin icon. --- mod/url/lib.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mod/url/lib.php b/mod/url/lib.php index dae393ac800..256c3eb516d 100644 --- a/mod/url/lib.php +++ b/mod/url/lib.php @@ -245,6 +245,8 @@ function url_get_coursemodule_info($coursemodule) { } $info->customdata['display'] = $display; + // The icon will be filtered if it will be the default module icon. + $info->customdata['filtericon'] = empty($info->icon); return $info; } From bc1d7e854be0b9c8baad4120a5912585db47c328 Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Mon, 13 Feb 2023 13:17:18 +0800 Subject: [PATCH 11/11] MDL-77105 core: Add upgrade.txt notes --- lib/upgrade.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/upgrade.txt b/lib/upgrade.txt index 36463e05d6e..cba147fd7aa 100644 --- a/lib/upgrade.txt +++ b/lib/upgrade.txt @@ -5,6 +5,17 @@ information provided here is intended especially for developers. * Added 'extrainfo' in the DB options config. Its extra information for the DB driver, e.g. SQL Server, has additional configuration according to its environment, which the administrator can specify to alter and override any connection options. +* New \core_component::has_monologo_icon() that determines whether a plugin has monologo icons. This can be used to + determine whether to apply CSS filtering to the plugin's icon when rendering it. +* \cm_info::get_icon_url() resolves the icon's file type and adds a `filtericon` parameter in the URL when rendering the monologo + version of the course module icon or when the plugin declares, via its `filtericon` custom data, that the icon needs to be + filtered. This additional information can be used by plugins when rendering the module icon to determine whether to apply + CSS filtering to the icon. +* Activity plugins displaying activity module icons using \cm_info::get_icon_url() can declare the `filtericon` custom data in their + `get_coursemodule_info()` callback. If set, this will be used by \cm_info::get_icon_url() to set the icon URL's `filtericon` + parameter. This information can be used by the plugins when enclosing the icons in `.activityiconcontainer .icon` or + `.activityiconcontainer .activityicon` containers to determine whether CSS filtering should be applied to the icon. If the icon + needs to be rendered as is and not whitened out, the `.nofilter` CSS class needs to be applied to the icon. === 4.1.2 ===