diff --git a/commera/api/admin/plugins.py b/commera/api/admin/plugins.py index 07448ebf..474a0cdb 100644 --- a/commera/api/admin/plugins.py +++ b/commera/api/admin/plugins.py @@ -7,6 +7,7 @@ from frappe.query_builder.functions import Count from frappe.utils.data import cint +from commera import storefront_plugins from commera.api.admin.docfields import build_field_groups, get_editable_docfields, get_missing_fields from commera.api.admin.integrations import write_settings from commera.plugin_events import get_plugin_apps @@ -33,6 +34,7 @@ def get_plugins() -> list[dict]: registry = get_registry() failed_deliveries = get_failed_delivery_counts() + storefront_switches = storefront_plugins.get_storefront_switches() return [ { "app": app, @@ -46,11 +48,19 @@ def get_plugins() -> list[dict]: ], "problems": [problem["message"] for problem in registry["problems"] if problem["app"] == app], "failed_deliveries": failed_deliveries.get(app, 0), + # None for an app with no storefront hook; else whether its scripts and blocks load on store pages. + "storefront": storefront_switches.get(app), } for app in dict.fromkeys([*get_plugin_apps(), *registry["apps"]]) ] +@frappe.whitelist(methods=["POST"]) +def set_storefront_enabled(app: str, enabled: bool) -> None: + frappe.only_for("System Manager") + storefront_plugins.set_storefront_enabled(app, enabled) + + @frappe.whitelist() def get_plugin_deliveries(app: str, status: str | None = None, start: int = 0, page_length: int = 20) -> dict: frappe.only_for("System Manager") diff --git a/commera/storefront_plugins.py b/commera/storefront_plugins.py index 9312bd90..65aed9a9 100644 --- a/commera/storefront_plugins.py +++ b/commera/storefront_plugins.py @@ -4,6 +4,7 @@ import os import frappe +from frappe import _ from markupsafe import Markup from commera.guest import is_guest @@ -123,6 +124,22 @@ def sync_storefront_apps(app_name: str | None = None): clear_storefront_plugin_cache() +def get_storefront_switches() -> dict[str, bool]: + """Each app that declares a storefront hook, and whether the merchant has it switched on.""" + settings = frappe.get_cached_doc(SETTINGS) + return {row.app: bool(row.enabled) for row in settings.storefront_apps} + + +def set_storefront_enabled(app: str, enabled: bool): + settings = frappe.get_single(SETTINGS) + row = next((row for row in settings.storefront_apps if row.app == app), None) + if not row: + frappe.throw(_("{0} adds nothing to the storefront.").format(app)) + row.enabled = int(enabled) + settings.save() + clear_storefront_plugin_cache() + + def clear_storefront_plugin_cache(): frappe.cache.delete_value(CACHE_KEY) diff --git a/commera/tests/test_storefront_plugins.py b/commera/tests/test_storefront_plugins.py index 241a4959..e6acd8de 100644 --- a/commera/tests/test_storefront_plugins.py +++ b/commera/tests/test_storefront_plugins.py @@ -6,6 +6,7 @@ from frappe.utils import get_html_for_route, set_request from commera import storefront_plugins +from commera.api.admin.plugins import set_storefront_enabled BANNER = "commera/tests/storefront_plugin_templates/banner.html" BROKEN = "commera/tests/storefront_plugin_templates/broken.html" @@ -77,6 +78,27 @@ def test_switching_on_renders_tags_and_blocks(self): currency = frappe.db.get_single_value("Global Defaults", "default_currency") self.assertIn(f"{store_name}|{currency}|{frappe.local.lang}|False", banner) + def test_the_switch_turns_a_plugin_on_and_off_the_storefront(self): + self.declare_full_plugin() + + set_storefront_enabled(app="commera", enabled=True) + self.assertEqual(storefront_plugins.get_storefront_switches()["commera"], True) + self.assertIn(SCRIPT, storefront_plugins.format_plugin_includes("js")) + + set_storefront_enabled(app="commera", enabled=False) + self.assertEqual(storefront_plugins.format_plugin_includes("js"), "") + + def test_the_switch_refuses_an_app_without_storefront_hooks(self): + with self.assertRaises(frappe.ValidationError): + set_storefront_enabled(app="frappe", enabled=True) + + def test_only_a_system_manager_turns_the_switch(self): + self.declare_full_plugin() + frappe.set_user("Guest") + self.addCleanup(frappe.set_user, "Administrator") + with self.assertRaises(frappe.PermissionError): + set_storefront_enabled(app="commera", enabled=True) + def test_asset_outside_the_app_is_refused(self): self.declare_hooks( { diff --git a/dashboard/src/components/settings/PluginsSettings.vue b/dashboard/src/components/settings/PluginsSettings.vue index 9f866e49..e7178666 100644 --- a/dashboard/src/components/settings/PluginsSettings.vue +++ b/dashboard/src/components/settings/PluginsSettings.vue @@ -1,13 +1,13 @@