mirror of
https://github.com/searxng/searxng.git
synced 2025-02-21 04:39:02 +00:00
[refactor] plugins: allow setting default enabled state via settings.yml
This commit is contained in:
parent
4ab7984edd
commit
37716f47bb
@ -30,7 +30,6 @@ Configuration defaults (at built time):
|
||||
{% for plg in plugins %}
|
||||
|
||||
* - {{plg.info.name}}
|
||||
- {{(plg.default_on and "y") or ""}}
|
||||
- {{plg.info.description}}
|
||||
|
||||
{% endfor %}
|
||||
|
@ -27,13 +27,21 @@ configuration looks like:
|
||||
.. code:: yaml
|
||||
|
||||
enabled_plugins:
|
||||
- 'Basic Calculator'
|
||||
- 'Hash plugin'
|
||||
- 'Self Information'
|
||||
- 'Tracker URL remover'
|
||||
- 'Unit converter plugin'
|
||||
- 'Ahmia blacklist'
|
||||
- name: 'Basic Calculator'
|
||||
default_on: true
|
||||
- name: 'Hash plugin'
|
||||
default_on: true
|
||||
- name: 'Self Information'
|
||||
default_on: true
|
||||
- name: 'Tracker URL remover'
|
||||
default_on: true
|
||||
- name: 'Unit converter plugin'
|
||||
default_on: true
|
||||
- name: 'Ahmia blacklist' # activation depends on outgoing.using_tor_proxy
|
||||
default_on: true
|
||||
|
||||
In order to disable a plugin by default, but still allow users to use it by enabling
|
||||
it in their user settings, set ``default_on`` to ``false``.
|
||||
|
||||
.. _settings external_plugins:
|
||||
|
||||
|
@ -18,7 +18,6 @@ area:
|
||||
class MyPlugin(Plugin):
|
||||
|
||||
id = "self_info"
|
||||
default_on = True
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
@ -75,9 +75,6 @@ class Plugin(abc.ABC):
|
||||
id: typing.ClassVar[str]
|
||||
"""The ID (suffix) in the HTML form."""
|
||||
|
||||
default_on: typing.ClassVar[bool]
|
||||
"""Plugin is enabled/disabled by default."""
|
||||
|
||||
keywords: list[str] = []
|
||||
"""Keywords in the search query that activate the plugin. The *keyword* is
|
||||
the first word in a search query. If a plugin should be executed regardless
|
||||
@ -94,9 +91,8 @@ class Plugin(abc.ABC):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
for attr in ["id", "default_on"]:
|
||||
if getattr(self, attr, None) is None:
|
||||
raise NotImplementedError(f"plugin {self} is missing attribute {attr}")
|
||||
if getattr(self, "id", None) is None:
|
||||
raise NotImplementedError(f"plugin {self} is missing attribute id")
|
||||
|
||||
if not self.id:
|
||||
self.id = f"{self.__class__.__module__}.{self.__class__.__name__}"
|
||||
@ -117,6 +113,26 @@ class Plugin(abc.ABC):
|
||||
|
||||
return hash(self) == hash(other)
|
||||
|
||||
def is_enabled_by_default(self) -> bool:
|
||||
"""
|
||||
Check whether a plugin is enabled by default based on the instance's configuration
|
||||
|
||||
This method may not be overriden in any plugin implementation!
|
||||
"""
|
||||
enabled_plugins = searx.get_setting('enabled_plugins', [])
|
||||
if not enabled_plugins:
|
||||
return False
|
||||
|
||||
for enabled_plugin in enabled_plugins:
|
||||
if isinstance(enabled_plugin, dict):
|
||||
# for legacy reasons, it's still allowed to reference plugins by their
|
||||
# name instead of their ID in the settings
|
||||
if enabled_plugin.get('name') in (self.info.name, self.id):
|
||||
return enabled_plugin.get('default_on', True)
|
||||
|
||||
# legacy way of enabling plugins (list of strings) - TODO: remove in the future
|
||||
return self.info.name in enabled_plugins
|
||||
|
||||
def init(self, app: flask.Flask) -> bool: # pylint: disable=unused-argument
|
||||
"""Initialization of the plugin, the return value decides whether this
|
||||
plugin is active or not. Initialization only takes place once, at the
|
||||
@ -176,7 +192,7 @@ class ModulePlugin(Plugin):
|
||||
- `module.logger` --> :py:obj:`Plugin.log`
|
||||
"""
|
||||
|
||||
_required_attrs = (("name", str), ("description", str), ("default_on", bool))
|
||||
_required_attrs = (("name", str), ("description", str))
|
||||
|
||||
def __init__(self, mod: types.ModuleType):
|
||||
"""In case of missing attributes in the module or wrong types are given,
|
||||
@ -197,7 +213,6 @@ class ModulePlugin(Plugin):
|
||||
self.log.critical(msg)
|
||||
raise TypeError(msg)
|
||||
|
||||
self.default_on = mod.default_on
|
||||
self.info = PluginInfo(
|
||||
id=self.id,
|
||||
name=self.module.name,
|
||||
@ -291,6 +306,8 @@ class PluginStorage:
|
||||
"""Register a :py:obj:`Plugin`. In case of name collision (if two
|
||||
plugins have same ID) a :py:obj:`KeyError` exception is raised.
|
||||
"""
|
||||
if not self.plugin_enabled(plugin):
|
||||
return
|
||||
|
||||
if plugin in self.plugin_list:
|
||||
msg = f"name collision '{plugin.id}'"
|
||||
@ -329,6 +346,28 @@ class PluginStorage:
|
||||
|
||||
self.register(code_obj())
|
||||
|
||||
def plugin_enabled(self, plugin: searx.plugins.Plugin) -> bool:
|
||||
"""
|
||||
Check whether a plugin is enabled based on the instance's configuration
|
||||
"""
|
||||
enabled_plugins = searx.get_setting('enabled_plugins', [])
|
||||
if not enabled_plugins:
|
||||
return False
|
||||
|
||||
for enabled_plugin in enabled_plugins:
|
||||
if isinstance(enabled_plugin, dict):
|
||||
# for legacy reasons, it's still allowed to reference plugins by their
|
||||
# name instead of their ID in the settings
|
||||
if enabled_plugin.get('name') in (plugin.info.name, plugin.id):
|
||||
return True
|
||||
|
||||
# legacy way of enabling plugins - TODO: remove in the future
|
||||
elif isinstance(enabled_plugin, str):
|
||||
if enabled_plugin == plugin.info.name:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def init(self, app: flask.Flask) -> None:
|
||||
"""Calls the method :py:obj:`Plugin.init` of each plugin in this
|
||||
storage. Depending on its return value, the plugin is removed from
|
||||
|
@ -12,7 +12,6 @@ from searx import get_setting
|
||||
|
||||
name = "Ahmia blacklist"
|
||||
description = "Filter out onion results that appear in Ahmia's blacklist. (See https://ahmia.fi/blacklist)"
|
||||
default_on = True
|
||||
preference_section = 'onions'
|
||||
|
||||
ahmia_blacklist: list = []
|
||||
|
@ -18,7 +18,6 @@ from searx.result_types import EngineResults
|
||||
|
||||
name = "Basic Calculator"
|
||||
description = gettext("Calculate mathematical expressions via the search bar")
|
||||
default_on = True
|
||||
preference_section = 'general'
|
||||
plugin_id = 'calculator'
|
||||
|
||||
|
@ -22,7 +22,6 @@ class SXNGPlugin(Plugin):
|
||||
"""
|
||||
|
||||
id = "hash_plugin"
|
||||
default_on = True
|
||||
keywords = ["md5", "sha1", "sha224", "sha256", "sha384", "sha512"]
|
||||
|
||||
def __init__(self):
|
||||
|
@ -12,7 +12,8 @@ The **Hostnames plugin** can be enabled by adding it to the
|
||||
.. code:: yaml
|
||||
|
||||
enabled_plugins:
|
||||
- 'Hostnames plugin'
|
||||
- name: 'Hostnames plugin'
|
||||
default_on: true
|
||||
...
|
||||
|
||||
- ``hostnames.replace``: A **mapping** of regular expressions to hostnames to be
|
||||
@ -104,7 +105,6 @@ from searx.settings_loader import get_yaml_cfg
|
||||
|
||||
name = gettext('Hostnames plugin')
|
||||
description = gettext('Rewrite hostnames, remove results or prioritize them based on the hostname')
|
||||
default_on = False
|
||||
preference_section = 'general'
|
||||
|
||||
plugin_id = 'hostnames'
|
||||
|
@ -14,7 +14,6 @@ regex = re.compile(r'10\.\d{4,9}/[^\s]+')
|
||||
|
||||
name = gettext('Open Access DOI rewrite')
|
||||
description = gettext('Avoid paywalls by redirecting to open-access versions of publications when available')
|
||||
default_on = False
|
||||
preference_section = 'general/doi_resolver'
|
||||
|
||||
|
||||
|
@ -23,7 +23,6 @@ class SXNGPlugin(Plugin):
|
||||
"""
|
||||
|
||||
id = "self_info"
|
||||
default_on = True
|
||||
keywords = ["ip", "user-agent"]
|
||||
|
||||
def __init__(self):
|
||||
|
@ -10,7 +10,8 @@ Enable in ``settings.yml``:
|
||||
|
||||
enabled_plugins:
|
||||
..
|
||||
- 'Tor check plugin'
|
||||
- name: 'Tor check plugin'
|
||||
default_on: true
|
||||
|
||||
"""
|
||||
|
||||
@ -24,8 +25,6 @@ from searx.network import get
|
||||
from searx.result_types import Answer
|
||||
|
||||
|
||||
default_on = False
|
||||
|
||||
name = gettext("Tor check plugin")
|
||||
'''Translated name of the plugin'''
|
||||
|
||||
|
@ -17,7 +17,6 @@ regexes = {
|
||||
|
||||
name = gettext('Tracker URL remover')
|
||||
description = gettext('Remove trackers arguments from the returned URL')
|
||||
default_on = True
|
||||
preference_section = 'privacy'
|
||||
|
||||
|
||||
|
@ -14,7 +14,8 @@ Enable in ``settings.yml``:
|
||||
|
||||
enabled_plugins:
|
||||
..
|
||||
- 'Unit converter plugin'
|
||||
- name: 'Unit converter plugin'
|
||||
default_on: true
|
||||
|
||||
"""
|
||||
|
||||
@ -30,7 +31,6 @@ from searx.result_types import Answer
|
||||
|
||||
name = "Unit converter plugin"
|
||||
description = gettext("Convert between units")
|
||||
default_on = True
|
||||
|
||||
plugin_id = "unit_converter"
|
||||
preference_section = "general"
|
||||
|
@ -316,7 +316,8 @@ class PluginsSetting(BooleanChoices):
|
||||
"""Plugin settings"""
|
||||
|
||||
def __init__(self, default_value, plugins: Iterable[searx.plugins.Plugin]):
|
||||
super().__init__(default_value, {plugin.id: plugin.default_on for plugin in plugins})
|
||||
plugin_states = {plugin.id: plugin.is_enabled_by_default() for plugin in plugins}
|
||||
super().__init__(default_value, plugin_states)
|
||||
|
||||
def transform_form_items(self, items):
|
||||
return [item[len('plugin_') :] for item in items]
|
||||
|
@ -234,21 +234,30 @@ outgoing:
|
||||
# - mypackage.mymodule.MyOtherPlugin
|
||||
# - ...
|
||||
|
||||
# Comment or un-comment plugin to activate / deactivate by default.
|
||||
# https://docs.searxng.org/admin/settings/settings_plugins.html
|
||||
#
|
||||
# enabled_plugins:
|
||||
# # these plugins are enabled if nothing is configured ..
|
||||
# - 'Basic Calculator'
|
||||
# - 'Hash plugin'
|
||||
# - 'Self Information'
|
||||
# - 'Tracker URL remover'
|
||||
# - 'Unit converter plugin'
|
||||
# - 'Ahmia blacklist' # activation depends on outgoing.using_tor_proxy
|
||||
# # these plugins are disabled if nothing is configured ..
|
||||
# - 'Hostnames plugin' # see 'hostnames' configuration below
|
||||
# - 'Open Access DOI rewrite'
|
||||
# - 'Tor check plugin'
|
||||
# Comment plugins out to completely disable them.
|
||||
# Set 'default_on' to false in order to disable them by default,
|
||||
# but allow users to manually enable them in the settings.
|
||||
# see https://docs.searxng.org/admin/settings/settings_plugins.html
|
||||
enabled_plugins:
|
||||
- name: 'Basic Calculator'
|
||||
default_on: true
|
||||
- name: 'Hash plugin'
|
||||
default_on: true
|
||||
- name: 'Self Information'
|
||||
default_on: true
|
||||
- name: 'Tracker URL remover'
|
||||
default_on: true
|
||||
- name: 'Unit converter plugin'
|
||||
default_on: true
|
||||
- name: 'Ahmia blacklist' # activation depends on outgoing.using_tor_proxy
|
||||
default_on: true
|
||||
# these plugins are completely disabled if nothing is configured ..
|
||||
# - name: 'Hostnames plugin' # see 'hostnames' configuration below
|
||||
# default_on: false
|
||||
# - name: 'Open Access DOI rewrite'
|
||||
# default_on: false
|
||||
# - name: 'Tor check plugin'
|
||||
# default_on: false
|
||||
|
||||
# Configuration of the "Hostnames plugin":
|
||||
#
|
||||
|
@ -1292,7 +1292,7 @@ def config():
|
||||
|
||||
_plugins = []
|
||||
for _ in searx.plugins.STORAGE:
|
||||
_plugins.append({'name': _.id, 'enabled': _.default_on})
|
||||
_plugins.append({'name': _.id})
|
||||
|
||||
_limiter_cfg = limiter.get_cfg()
|
||||
|
||||
|
@ -47,10 +47,16 @@ def do_post_search(query, storage, **kwargs) -> Mock:
|
||||
|
||||
class PluginMock(searx.plugins.Plugin):
|
||||
|
||||
def __init__(self, _id: str, name: str, default_on: bool):
|
||||
def __init__(self, _id: str, name: str, default_enabled: bool = False):
|
||||
self.id = _id
|
||||
self.default_on = default_on
|
||||
self._name = name
|
||||
self.default_enabled = default_enabled
|
||||
self.info = searx.plugins.PluginInfo(
|
||||
id=id,
|
||||
name=name,
|
||||
description=f"Dummy plugin: {id}",
|
||||
preference_section="general",
|
||||
)
|
||||
super().__init__()
|
||||
|
||||
# pylint: disable= unused-argument
|
||||
@ -63,14 +69,6 @@ class PluginMock(searx.plugins.Plugin):
|
||||
def on_result(self, request, search, result) -> bool:
|
||||
return False
|
||||
|
||||
def info(self):
|
||||
return searx.plugins.PluginInfo(
|
||||
id=self.id,
|
||||
name=self._name,
|
||||
description=f"Dummy plugin: {self.id}",
|
||||
preference_section="general",
|
||||
)
|
||||
|
||||
|
||||
class PluginStorage(SearxTestCase):
|
||||
|
||||
@ -78,9 +76,17 @@ class PluginStorage(SearxTestCase):
|
||||
super().setUp()
|
||||
engines = {}
|
||||
|
||||
searx.settings['enabled_plugins'] = [
|
||||
{
|
||||
'name': 'plg001',
|
||||
},
|
||||
{
|
||||
'name': 'plg002',
|
||||
},
|
||||
]
|
||||
self.storage = searx.plugins.PluginStorage()
|
||||
self.storage.register(PluginMock("plg001", "first plugin", True))
|
||||
self.storage.register(PluginMock("plg002", "second plugin", True))
|
||||
self.storage.register(PluginMock("plg001", "first plugin"))
|
||||
self.storage.register(PluginMock("plg002", "second plugin"))
|
||||
self.storage.init(self.app)
|
||||
self.pref = searx.preferences.Preferences(["simple"], ["general"], engines, self.storage)
|
||||
self.pref.parse_dict({"locale": "en"})
|
||||
|
@ -115,19 +115,26 @@ class TestSettings(SearxTestCase):
|
||||
|
||||
# plugins settings
|
||||
|
||||
def test_plugins_setting_all_default_enabled(self):
|
||||
storage = searx.plugins.PluginStorage()
|
||||
storage.register(PluginMock("plg001", "first plugin", True))
|
||||
storage.register(PluginMock("plg002", "second plugin", True))
|
||||
plgs_settings = PluginsSetting(False, storage)
|
||||
self.assertEqual(set(plgs_settings.get_enabled()), {"plg001", "plg002"})
|
||||
|
||||
def test_plugins_setting_few_default_enabled(self):
|
||||
searx.settings['enabled_plugins'] = [
|
||||
{
|
||||
'name': 'plg001',
|
||||
},
|
||||
{
|
||||
'name': 'plg002',
|
||||
'default_on': False,
|
||||
},
|
||||
{
|
||||
'name': 'plg003',
|
||||
'default_on': True,
|
||||
},
|
||||
]
|
||||
storage = searx.plugins.PluginStorage()
|
||||
storage.register(PluginMock("plg001", "first plugin", True))
|
||||
storage.register(PluginMock("plg002", "second plugin", False))
|
||||
storage.register(PluginMock("plg003", "third plugin", True))
|
||||
storage.register(PluginMock("plg001", "first plugin"))
|
||||
storage.register(PluginMock("plg002", "second plugin"))
|
||||
storage.register(PluginMock("plg003", "third plugin"))
|
||||
plgs_settings = PluginsSetting(False, storage)
|
||||
self.assertEqual(set(plgs_settings.get_disabled()), set(['plg002']))
|
||||
self.assertEqual(set(plgs_settings.get_enabled()), set(['plg001', 'plg003']))
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user