diff --git a/docs/themes.rst b/docs/themes.rst
index 51b5b0d5..2df717e4 100644
--- a/docs/themes.rst
+++ b/docs/themes.rst
@@ -140,6 +140,23 @@ your date according to the locale given in your settings::
.. _datetime: https://docs.python.org/3/library/datetime.html#datetime-objects
.. _strftime: https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior
+Checking Loaded Plugins
+-----------------------
+
+Pelican provides a ``plugin_enabled`` Jinja test for checking if a certain plugin
+is enabled. This test accepts a plugin name as a string and will return a boolean.
+Namespace Plugins can be specified with their full name (``pelican.plugins.plugin_name``)
+or their short name (``plugin_name``). The following example uses ``webassets`` plugin
+to minify CSS if it is enabled and falls back to regular CSS otherwise::
+
+ {% if "webassets" is plugin_enabled %}
+ {% assets filters="cssmin", output="css/style.min.css", "css/style.scss" %}
+
+ {% endassets %}
+ {% else %}
+
+ {% endif %}
+
index.html
----------
diff --git a/pelican/generators.py b/pelican/generators.py
index 0bbb7268..3b5ca9e4 100644
--- a/pelican/generators.py
+++ b/pelican/generators.py
@@ -20,6 +20,7 @@ from jinja2 import (
from pelican.cache import FileStampDataCacher
from pelican.contents import Article, Page, Static
from pelican.plugins import signals
+from pelican.plugins._utils import plugin_enabled
from pelican.readers import Readers
from pelican.utils import (
DateFormatter,
@@ -102,6 +103,9 @@ class Generator:
# get custom Jinja tests from user settings
custom_tests = self.settings["JINJA_TESTS"]
+ self.env.tests["plugin_enabled"] = partial(
+ plugin_enabled, plugin_list=self.settings["PLUGINS"]
+ )
self.env.tests.update(custom_tests)
signals.generator_init.send(self)
diff --git a/pelican/plugins/_utils.py b/pelican/plugins/_utils.py
index f0c18f5c..c25f8114 100644
--- a/pelican/plugins/_utils.py
+++ b/pelican/plugins/_utils.py
@@ -40,6 +40,22 @@ def list_plugins(ns_pkg=None):
logger.info("No plugins are installed")
+def plugin_enabled(name, plugin_list=None):
+ if plugin_list is None or not plugin_list:
+ # no plugins are loaded
+ return False
+
+ if name in plugin_list:
+ # search name as is
+ return True
+
+ if "pelican.plugins.{}".format(name) in plugin_list:
+ # check if short name is a namespace plugin
+ return True
+
+ return False
+
+
def load_legacy_plugin(plugin, plugin_paths):
if "." in plugin:
# it is in a package, try to resolve package first
diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py
index 52adb2c9..af6f5b1a 100644
--- a/pelican/tests/test_generators.py
+++ b/pelican/tests/test_generators.py
@@ -1581,6 +1581,31 @@ class TestJinja2Environment(TestCaseWithCLocale):
self._test_jinja2_helper(settings, content, expected)
+ def test_jinja2_filter_plugin_enabled(self):
+ """JINJA_FILTERS adds custom filters to Jinja2 environment"""
+ settings = {"PLUGINS": ["legacy_plugin", "pelican.plugins.ns_plugin"]}
+ jinja_template = (
+ "{plugin}: "
+ "{{% if '{plugin}' is plugin_enabled %}}yes"
+ "{{% else %}}no{{% endif %}}"
+ )
+ content = " / ".join(
+ (
+ jinja_template.format(plugin="ns_plugin"),
+ jinja_template.format(plugin="pelican.plugins.ns_plugin"),
+ jinja_template.format(plugin="legacy_plugin"),
+ jinja_template.format(plugin="unknown"),
+ )
+ )
+ expected = (
+ "ns_plugin: yes / "
+ "pelican.plugins.ns_plugin: yes / "
+ "legacy_plugin: yes / "
+ "unknown: no"
+ )
+
+ self._test_jinja2_helper(settings, content, expected)
+
def test_jinja2_test(self):
"""JINJA_TESTS adds custom tests to Jinja2 environment"""
content = "foo {{ foo is custom_test }}, bar {{ bar is custom_test }}"
diff --git a/pelican/tests/test_plugins.py b/pelican/tests/test_plugins.py
index 4f02022c..ccce684e 100644
--- a/pelican/tests/test_plugins.py
+++ b/pelican/tests/test_plugins.py
@@ -2,7 +2,12 @@ import os
from contextlib import contextmanager
import pelican.tests.dummy_plugins.normal_plugin.normal_plugin as normal_plugin
-from pelican.plugins._utils import get_namespace_plugins, get_plugin_name, load_plugins
+from pelican.plugins._utils import (
+ get_namespace_plugins,
+ get_plugin_name,
+ load_plugins,
+ plugin_enabled,
+)
from pelican.tests.support import unittest
@@ -183,3 +188,78 @@ class PluginTest(unittest.TestCase):
get_plugin_name(NoopPlugin()),
"PluginTest.test_get_plugin_name..NoopPlugin",
)
+
+ def test_plugin_enabled(self):
+ def get_plugin_names(plugins):
+ return [get_plugin_name(p) for p in plugins]
+
+ with tmp_namespace_path(self._NS_PLUGIN_FOLDER):
+ # with no `PLUGINS` setting, load namespace plugins
+ SETTINGS = {}
+ plugins = get_plugin_names(load_plugins(SETTINGS))
+ self.assertTrue(plugin_enabled("ns_plugin", plugins))
+ self.assertTrue(plugin_enabled("pelican.plugins.ns_plugin", plugins))
+ self.assertFalse(plugin_enabled("normal_plugin", plugins))
+ self.assertFalse(plugin_enabled("unknown", plugins))
+
+ # disable namespace plugins with `PLUGINS = []`
+ SETTINGS = {"PLUGINS": []}
+ plugins = get_plugin_names(load_plugins(SETTINGS))
+ self.assertFalse(plugin_enabled("ns_plugin", plugins))
+ self.assertFalse(plugin_enabled("pelican.plugins.ns_plugin", plugins))
+ self.assertFalse(plugin_enabled("normal_plugin", plugins))
+ self.assertFalse(plugin_enabled("unknown", plugins))
+
+ # with `PLUGINS`, load only specified plugins
+
+ # normal plugin
+ SETTINGS = {
+ "PLUGINS": ["normal_plugin"],
+ "PLUGIN_PATHS": [self._NORMAL_PLUGIN_FOLDER],
+ }
+ plugins = get_plugin_names(load_plugins(SETTINGS))
+ self.assertFalse(plugin_enabled("ns_plugin", plugins))
+ self.assertFalse(plugin_enabled("pelican.plugins.ns_plugin", plugins))
+ self.assertTrue(plugin_enabled("normal_plugin", plugins))
+ self.assertFalse(plugin_enabled("unknown", plugins))
+
+ # normal submodule/subpackage plugins
+ SETTINGS = {
+ "PLUGINS": [
+ "normal_submodule_plugin.subplugin",
+ "normal_submodule_plugin.subpackage.subpackage",
+ ],
+ "PLUGIN_PATHS": [self._NORMAL_PLUGIN_FOLDER],
+ }
+ plugins = get_plugin_names(load_plugins(SETTINGS))
+ self.assertFalse(plugin_enabled("ns_plugin", plugins))
+ self.assertFalse(plugin_enabled("pelican.plugins.ns_plugin", plugins))
+ self.assertFalse(plugin_enabled("normal_plugin", plugins))
+ self.assertFalse(plugin_enabled("unknown", plugins))
+
+ # namespace plugin short
+ SETTINGS = {"PLUGINS": ["ns_plugin"]}
+ plugins = get_plugin_names(load_plugins(SETTINGS))
+ self.assertTrue(plugin_enabled("ns_plugin", plugins))
+ self.assertTrue(plugin_enabled("pelican.plugins.ns_plugin", plugins))
+ self.assertFalse(plugin_enabled("normal_plugin", plugins))
+ self.assertFalse(plugin_enabled("unknown", plugins))
+
+ # namespace plugin long
+ SETTINGS = {"PLUGINS": ["pelican.plugins.ns_plugin"]}
+ plugins = get_plugin_names(load_plugins(SETTINGS))
+ self.assertTrue(plugin_enabled("ns_plugin", plugins))
+ self.assertTrue(plugin_enabled("pelican.plugins.ns_plugin", plugins))
+ self.assertFalse(plugin_enabled("normal_plugin", plugins))
+ self.assertFalse(plugin_enabled("unknown", plugins))
+
+ # normal and namespace plugin
+ SETTINGS = {
+ "PLUGINS": ["normal_plugin", "ns_plugin"],
+ "PLUGIN_PATHS": [self._NORMAL_PLUGIN_FOLDER],
+ }
+ plugins = get_plugin_names(load_plugins(SETTINGS))
+ self.assertTrue(plugin_enabled("ns_plugin", plugins))
+ self.assertTrue(plugin_enabled("pelican.plugins.ns_plugin", plugins))
+ self.assertTrue(plugin_enabled("normal_plugin", plugins))
+ self.assertFalse(plugin_enabled("unknown", plugins))