Browse Source

Add ability to disable built-in CRX settings (#635)

Building on the work from #623 

See settings:
* CRX_DISABLE_LAYOUT (not recommended)
* CRX_DISABLE_ANALYTICS
* CRX_DISABLE_NAVBAR
* CRX_DISABLE_FOOTER

The plan, based on what @jlchilders11 and I discussed, is to disable
navbar and footer in the `pro` template, and provide a simple
boilerplate navbar and footer for customization.

Note: template filters `crx_settings` and `django_settings` have been
removed, and replaced with tag ` {% django_setting "MY_SETTING" %}`.

---------

Co-authored-by: Michael Steigman <msteigman@mgh.harvard.edu>
Vince Salvino 10 months ago
parent
commit
42640abb95

+ 15 - 3
coderedcms/models/snippet_models.py

@@ -15,13 +15,25 @@ from wagtail.admin.panels import (
 from wagtail.models import Orderable
 from wagtail.snippets.models import register_snippet
 from wagtail.images import get_image_model_string
-
 from coderedcms.blocks import (
     HTML_STREAMBLOCKS,
     LAYOUT_STREAMBLOCKS,
     NAVIGATION_STREAMBLOCKS,
 )
 from coderedcms.fields import CoderedStreamField
+from coderedcms.settings import crx_settings
+
+
+def maybe_register_snippet(disable: bool, **kwargs):
+    """Decorator that conditionally registers a snippet model."""
+
+    def check_if_disabled(model):
+        if not disable:
+            register_snippet(model, **kwargs)
+
+        return model
+
+    return check_if_disabled
 
 
 @register_snippet
@@ -292,7 +304,7 @@ class FilmPanel(Orderable, models.Model):
     ]
 
 
-@register_snippet
+@maybe_register_snippet(crx_settings.CRX_DISABLE_NAVBAR)
 class Navbar(models.Model):
     """
     Snippet for site navigation bars (header, main menu, etc.)
@@ -338,7 +350,7 @@ class Navbar(models.Model):
         return self.name
 
 
-@register_snippet
+@maybe_register_snippet(crx_settings.CRX_DISABLE_FOOTER)
 class Footer(models.Model):
     """
     Snippet for website footer content.

+ 37 - 2
coderedcms/models/tests/test_wagtailsettings_models.py

@@ -1,9 +1,14 @@
-from django.test import Client
+from unittest.mock import patch
+
+from django.test import Client, override_settings
 from wagtail.test.utils import WagtailPageTests
 from wagtail.models import Site
 
 from coderedcms.tests.testapp.models import WebPage
-from coderedcms.models.wagtailsettings_models import AnalyticsSettings
+from coderedcms.models.wagtailsettings_models import (
+    AnalyticsSettings,
+    maybe_register_setting,
+)
 
 
 class AnalyticsSettingsTestCase(WagtailPageTests):
@@ -55,3 +60,33 @@ class AnalyticsSettingsTestCase(WagtailPageTests):
         """
         response = self.client.get(self.homepage.url, follow=True)
         self.assertInHTML(self.settings.body_scripts, str(response.content), 1)
+
+
+class MaybeRegisterSettingTestCase(WagtailPageTests):
+    """Testing the maybe_register_setting decorator.
+
+    These tests use a dummy settings object to test the two paths
+    through the decorator which determine whether or not register_settings
+    is called."""
+
+    def setUp(self):
+        super().setUp()
+        self.dummy_settings = object()
+
+    @override_settings(CRX_DISABLE_LAYOUT=False)
+    @patch("coderedcms.models.wagtailsettings_models.register_setting")
+    def test_decorator_enabled(self, mock_register_setting):
+        """Test that the decorator calls register_setting
+        when override setting is False."""
+        maybe_register_setting(False, icon="foobar")(self.dummy_settings)
+        mock_register_setting.assert_called_once_with(
+            self.dummy_settings, icon="foobar"
+        )
+
+    @override_settings(CRX_DISABLE_SITE_SETTINGS=True)
+    @patch("coderedcms.models.wagtailsettings_models.register_setting")
+    def test_decorator_disabled(self, mock_register_setting):
+        """Test that the decorator does not call register_setting
+        when override setting is False."""
+        maybe_register_setting(True, icon="foobar")(self.dummy_settings)
+        mock_register_setting.assert_not_called()

+ 38 - 16
coderedcms/models/wagtailsettings_models.py

@@ -22,7 +22,19 @@ from coderedcms.settings import crx_settings
 from coderedcms.models.snippet_models import Navbar, Footer
 
 
-@register_setting(icon="cr-desktop")
+def maybe_register_setting(disable: bool, **kwargs):
+    """Decorator that conditionally registers a settings class."""
+
+    def check_if_disabled(model):
+        if not disable:
+            register_setting(model, **kwargs)
+
+        return model
+
+    return check_if_disabled
+
+
+@maybe_register_setting(crx_settings.CRX_DISABLE_LAYOUT, icon="cr-desktop")
 class LayoutSettings(ClusterableModel, BaseSiteSetting):
     """
     Branding, navbar, and theme settings.
@@ -139,14 +151,7 @@ class LayoutSettings(ClusterableModel, BaseSiteSetting):
         help_text=_("The API Key used for Mailchimp."),
     )
 
-    panels = [
-        MultiFieldPanel(
-            [
-                FieldPanel("logo"),
-                FieldPanel("favicon"),
-            ],
-            heading=_("Branding"),
-        ),
+    navbar_panels = [
         MultiFieldPanel(
             [
                 FieldPanel("navbar_color_scheme"),
@@ -159,22 +164,35 @@ class LayoutSettings(ClusterableModel, BaseSiteSetting):
             ],
             heading=_("Site Navbar Layout"),
         ),
-        MultiFieldPanel(
-            [
-                FieldPanel("frontend_theme"),
-            ],
-            heading=_("Theming"),
-        ),
         InlinePanel(
             "site_navbar",
             help_text=_("Choose one or more navbars for your site."),
             heading=_("Site Navbars"),
         ),
+    ]
+
+    footer_panels = [
         InlinePanel(
             "site_footer",
             help_text=_("Choose one or more footers for your site."),
             heading=_("Site Footers"),
         ),
+    ]
+
+    panels = [
+        MultiFieldPanel(
+            [
+                FieldPanel("logo"),
+                FieldPanel("favicon"),
+            ],
+            heading=_("Branding"),
+        ),
+        MultiFieldPanel(
+            [
+                FieldPanel("frontend_theme"),
+            ],
+            heading=_("Theming"),
+        ),
         MultiFieldPanel(
             [
                 FieldPanel("from_email_address"),
@@ -191,6 +209,10 @@ class LayoutSettings(ClusterableModel, BaseSiteSetting):
             heading=_("API Keys"),
         ),
     ]
+    if not crx_settings.CRX_DISABLE_NAVBAR:
+        panels += navbar_panels
+    if not crx_settings.CRX_DISABLE_FOOTER:
+        panels += footer_panels
 
     def __init__(self, *args, **kwargs):
         """
@@ -256,7 +278,7 @@ class FooterOrderable(Orderable, models.Model):
     panels = [FieldPanel("footer")]
 
 
-@register_setting(icon="cr-google")
+@maybe_register_setting(crx_settings.CRX_DISABLE_ANALYTICS, icon="cr-google")
 class AnalyticsSettings(BaseSiteSetting):
     """
     Tracking and Google Analytics.

+ 5 - 0
coderedcms/settings.py

@@ -257,6 +257,11 @@ class _DefaultSettings:
     CRX_BANNER_BACKGROUND = "#ff0"
     CRX_BANNER_TEXT_COLOR = "#000"
 
+    CRX_DISABLE_ANALYTICS = False
+    CRX_DISABLE_LAYOUT = False
+    CRX_DISABLE_NAVBAR = False
+    CRX_DISABLE_FOOTER = False
+
     def __getattribute__(self, attr: str):
         # First load from Django settings.
         # If it does not exist, load from _DefaultSettings.

+ 3 - 1
coderedcms/templates/coderedcms/blocks/button_block.html

@@ -1,5 +1,7 @@
+{% load coderedcms_tags %}
+{% django_setting "CRX_DISABLE_ANALYTICS" as disable_analytics %}
 <a href="{{self.url}}"
-  {% if settings.coderedcms.AnalyticsSettings.ga_track_button_clicks %}
+  {% if not disable_analytics and settings.coderedcms.AnalyticsSettings.ga_track_button_clicks %}
   data-ga-event-category='{{self.settings.ga_tracking_event_category|default:"Button"}}'
   data-ga-event-label='{{self.settings.ga_tracking_event_label|default:self.button_title}}'
   {% endif %}

+ 3 - 2
coderedcms/templates/coderedcms/blocks/download_block.html

@@ -1,6 +1,7 @@
-{% load wagtailcore_tags %}
+{% load wagtailcore_tags coderedcms_tags %}
+{% django_setting "CRX_DISABLE_ANALYTICS" as disable_analytics %}
 <a href="{{self.downloadable_file.url}}" download="{{self.downloadable_file.url}}"
-  {% if settings.coderedcms.AnalyticsSettings.ga_track_button_clicks %}
+  {% if not disable_analytics and settings.coderedcms.AnalyticsSettings.ga_track_button_clicks %}
   data-ga-event-category='{{self.settings.ga_tracking_event_category|default:"Download"}}'
   data-ga-event-label='{{self.settings.ga_tracking_event_label|default:self.button_title}}'
   {% endif %}

+ 3 - 2
coderedcms/templates/coderedcms/blocks/image_link_block.html

@@ -1,6 +1,7 @@
-{% load wagtailimages_tags %}
+{% load coderedcms_tags wagtailimages_tags %}
+{% django_setting "CRX_DISABLE_ANALYTICS" as disable_analytics %}
 <a href="{{self.url}}"
-  {% if settings.coderedcms.AnalyticsSettings.ga_track_button_clicks %}
+  {% if not disable_analytics and settings.coderedcms.AnalyticsSettings.ga_track_button_clicks %}
   data-ga-event-category='{{self.settings.ga_tracking_event_category|default:"ImageLink"}}'
   data-ga-event-label='{{self.settings.ga_tracking_event_label|default:self.alt_text}}'
   {% endif %}

+ 6 - 3
coderedcms/templates/coderedcms/includes/crx_banner.html

@@ -1,6 +1,9 @@
 {% load coderedcms_tags %}
-{% if "CRX_BANNER"|crx_settings %}
-<div class="crx-banner" style="background-color:{{ 'CRX_BANNER_BACKGROUND'|crx_settings }}; color:{{ 'CRX_BANNER_TEXT_COLOR'|crx_settings }}; text-align:center; padding:4px;">
-  {{ "CRX_BANNER"|crx_settings|safe }}
+{% django_setting "CRX_BANNER" as crx_banner %}
+{% django_setting "CRX_BANNER_BACKGROUND" as crx_banner_background %}
+{% django_setting "CRX_BANNER_TEXT_COLOR" as crx_banner_text_color %}
+{% if crx_banner %}
+<div class="crx-banner" style="background-color:{{ crx_banner_background }}; color:{{ crx_banner_text_color }}; text-align:center; padding:4px;">
+  {{ crx_banner|safe }}
 </div>
 {% endif %}

+ 3 - 2
coderedcms/templates/coderedcms/includes/ical/calendar.html

@@ -1,4 +1,5 @@
 {% load coderedcms_tags %}
+{% django_setting "TIME_ZONE" as time_zone %}
 <div
   data-block="calendar"
   data-default-date="{% now 'Y-m-d' %}"
@@ -6,9 +7,9 @@
   data-event-display="{{ page.fullcalendar_event_display }}"
   data-event-source-url="{% url 'event_get_calendar_events' %}"
   data-page-id="{{ page.id }}"
-  data-timezone="{{ 'TIME_ZONE'|django_settings }}">
+  data-timezone="{{ time_zone }}">
   <noscript>JavaScript is required to view the Calendar.</noscript>
 </div>
 <div class="text-end text-muted">
-  <i>Listed in {{ 'TIME_ZONE'|django_settings }} time.</i>
+  <i>Listed in {{ time_zone }} time.</i>
 </div>

+ 15 - 6
coderedcms/templates/coderedcms/pages/base.html

@@ -2,12 +2,14 @@
 {% get_settings %}
 {% get_current_language as LANGUAGE_CODE %}
 {% wagtail_site as site %}
+{% django_setting "CRX_DISABLE_ANALYTICS" as disable_analytics %}
+{% django_setting "CRX_DISABLE_LAYOUT" as disable_layout %}
 <!doctype html>
 <html prefix="og: http://ogp.me/ns#" lang="{{ LANGUAGE_CODE }}">
 
 <head>
   {% block tracking %}
-  {% if settings.coderedcms.AnalyticsSettings.ga_g_tracking_id %}
+  {% if not disable_analytics and settings.coderedcms.AnalyticsSettings.ga_g_tracking_id %}
   <!-- Global site tag (gtag.js) - Google Analytics -->
   <script async src="https://www.googletagmanager.com/gtag/js?id={{settings.coderedcms.AnalyticsSettings.ga_g_tracking_id}}"></script>
   <script>
@@ -18,7 +20,7 @@
     gtag('config', '{{settings.coderedcms.AnalyticsSettings.ga_g_tracking_id}}');
   </script>
   {% endif %}
-  {% if settings.coderedcms.AnalyticsSettings.gtm_id %}
+  {% if not disable_analytics and settings.coderedcms.AnalyticsSettings.gtm_id %}
   <!-- Google Tag Manager -->
   <script>
     (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
@@ -29,7 +31,7 @@
   </script>
   <!-- End Google Tag Manager -->
   {% endif %}
-  {% if settings.coderedcms.AnalyticsSettings.head_scripts %}
+  {% if not disable_analytics and settings.coderedcms.AnalyticsSettings.head_scripts %}
   {{settings.coderedcms.AnalyticsSettings.head_scripts|safe}}
   {% endif %}
   {% endblock %}
@@ -37,8 +39,12 @@
   {# Pass in CMS variables to JavaScript #}
   <script>
     cr_site_url = "{{site.root_url}}";
-    cr_external_new_tab = {{settings.coderedcms.LayoutSettings.external_new_tab|yesno:"true,false"}};
     cr_version = "{% coderedcms_version %}";
+    {% if not disable_layout %}
+    cr_external_new_tab = {{settings.coderedcms.LayoutSettings.external_new_tab|yesno:"true,false"}};
+    {% else %}
+    cr_external_new_tab = false;
+    {% endif %}
   </script>
 
   <meta charset="utf-8">
@@ -56,7 +62,8 @@
   {% endblock %}
 
   {% block coderedcms_assets %}
-  {% if "DEBUG"|django_settings %}
+  {% django_setting "DEBUG" as debug %}
+  {% if debug %}
   <link rel="stylesheet" href="{% static 'coderedcms/css/crx-front.css' %}?v={% coderedcms_version %}">
   {% else %}
   <link rel="stylesheet" href="{% static 'coderedcms/css/crx-front.min.css' %}?v={% coderedcms_version %}">
@@ -66,7 +73,7 @@
   {% block custom_assets %}{% endblock %}
 
   {% block favicon %}
-  {% if settings.coderedcms.LayoutSettings.favicon %}
+  {% if not disable_layout and settings.coderedcms.LayoutSettings.favicon %}
   {# See https://developer.apple.com/design/human-interface-guidelines/ios/icons-and-images/app-icon/ #}
   {% image settings.coderedcms.LayoutSettings.favicon fill-256x256 format-webp preserve-svg as favicon_webp %}
   {% image settings.coderedcms.LayoutSettings.favicon fill-120x120 format-png preserve-svg as favicon_iphone %}
@@ -184,6 +191,7 @@
   {% endblock %}
 
   {% block body_tracking_scripts %}
+  {% if not disable_analytics %}
   {% if settings.coderedcms.AnalyticsSettings.gtm_id %}
   <!-- Google Tag Manager (noscript) -->
   <noscript><iframe src="https://www.googletagmanager.com/ns.html?id={{settings.coderedcms.AnalyticsSettings.gtm_id}}"
@@ -193,6 +201,7 @@
   {% if settings.coderedcms.AnalyticsSettings.body_scripts %}
   {{settings.coderedcms.AnalyticsSettings.body_scripts|safe}}
   {% endif %}
+  {% endif %}
   {% endblock %}
 </body>
 

+ 0 - 2
coderedcms/templates/coderedcms/pages/search.html

@@ -21,7 +21,6 @@
 {% block content_body %}
 <div class="container">
   {% block search_form %}
-  {% if not settings.coderedcms.LayoutSettings.navbar_search %}
   <form class="mt-5" action="{% url 'crx_search' %}" method="GET">
     <div class="row">
       <div class="col-8 col-sm-9">
@@ -32,7 +31,6 @@
       </div>
     </div>
   </form>
-  {% endif %}
   {% endblock search_form %}
 
   {% block search_page_types %}

+ 2 - 1
coderedcms/templates/coderedcms/snippets/footer.html

@@ -1,5 +1,6 @@
 {% load wagtailcore_tags coderedcms_tags %}
-{% if settings.coderedcms.LayoutSettings.site_footer %}
+{% django_setting "CRX_DISABLE_LAYOUT" as disable_layout %}
+{% if not disable_layout and settings.coderedcms.LayoutSettings.site_footer %}
 <footer>
   {% get_footers as footers %}
   {% for footer in footers %}

+ 14 - 0
coderedcms/templates/coderedcms/snippets/navbar.html

@@ -1,5 +1,7 @@
 {% load wagtailcore_tags wagtailsettings_tags wagtailimages_tags coderedcms_tags i18n %}
 {% wagtail_site as site %}
+{% django_setting "CRX_DISABLE_LAYOUT" as disable_layout %}
+{% if not disable_layout %}
 <nav class="navbar {% get_navbar_css %}">
   <div class="container{% if settings.coderedcms.LayoutSettings.navbar_content_fluid %}-fluid{% endif %}">
     <a class="navbar-brand" href="/">
@@ -49,3 +51,15 @@
 {% endif %}
 {% endif %}
 {% endblock %}
+
+{% else %}
+
+<nav class="navbar">
+  <div class="container">
+    <a class="navbar-brand" href="/">
+      {{site.site_name}}
+    </a>
+  </div>
+</nav>
+
+{% endif %}

+ 3 - 2
coderedcms/templates/wagtailadmin/base.html

@@ -1,5 +1,6 @@
 {% extends "wagtailadmin/base.html" %}
-{% load wagtailimages_tags wagtailsettings_tags %}
+{% load coderedcms_tags wagtailimages_tags wagtailsettings_tags %}
+{% django_setting "CRX_DISABLE_LAYOUT" as disable_layout %}
 
 {% block furniture %}
     {% include "coderedcms/includes/crx_banner.html" %}
@@ -8,4 +9,4 @@
 
 {# NOTE: this must be on a single line, otherwise Django templates will create
   whitespace, and wagtail will think the whitespace is the custom logo! #}
-{% block branding_logo %}{% if settings.coderedcms.LayoutSettings.logo %}<div class="crx-logo-container {{settings.coderedcms.LayoutSettings.navbar_color_scheme}}">{% image settings.coderedcms.LayoutSettings.logo original format-webp preserve-svg as logo_image %}<img src="{{ logo_image.url }}" class="crx-logo-custom" alt="Dashboard"></div>{% endif %}{% endblock %}
+{% block branding_logo %}{% if not disable_layout and settings.coderedcms.LayoutSettings.logo %}<div class="crx-logo-container {{settings.coderedcms.LayoutSettings.navbar_color_scheme}}">{% image settings.coderedcms.LayoutSettings.logo original format-webp preserve-svg as logo_image %}<img src="{{ logo_image.url }}" class="crx-logo-custom" alt="Dashboard"></div>{% endif %}{% endblock %}

+ 2 - 1
coderedcms/templates/wagtailadmin/login.html

@@ -1,3 +1,4 @@
 {% extends "wagtailadmin/login.html" %}
 {% load coderedcms_tags i18n %}
-{% block branding_login %}{% trans 'Sign in to' %} {{'WAGTAIL_SITE_NAME'|django_settings}}{% endblock %}
+{% django_setting "WAGTAIL_SITE_NAME" as wagtail_site_name %}
+{% block branding_login %}{% trans 'Sign in to' %} {{ wagtail_site_name }}{% endblock %}

+ 2 - 7
coderedcms/templatetags/coderedcms_tags.py

@@ -126,18 +126,13 @@ def process_form_cell(request, cell):
     return cell
 
 
-@register.filter
-def crx_settings(value):
-    return getattr(crx_settings_obj, value)
-
-
 @register.filter
 def bootstrap_settings(value):
     return get_bootstrap_setting(value)
 
 
-@register.filter
-def django_settings(value):
+@register.simple_tag
+def django_setting(value):
     return getattr(crx_settings_obj, value)
 
 

+ 30 - 0
docs/reference/django_settings.rst

@@ -38,6 +38,36 @@ CRX_BANNER_TEXT_COLOR
 String of a valid CSS color code to change the text color of the banner.
 
 
+CRX_DISABLE_ANALYTICS
+---------------------
+
+If set to ``True``, the ``AnalyticsSettings`` model ( "Tracking" in Wagtail Admin) will not be registered. All tracking and analytics features will be disabled. Note that all buttons and links throughout CRX have "Advanced Settings" to customize the tracking behavior --- if disabled these will do nothing. Defaults to ``False``.
+
+
+CRX_DISABLE_LAYOUT
+------------------
+
+If set to ``True``, the ``LayoutSettings`` model ( "CRX Settings" in Wagtail Admin) will not be registered. Many features such as the ability to set a logo, favion, Google Maps keys, etc. will not be available.
+
+If you're looking to customize the navbar and footer, it is NOT recommended to disable this, but instead use the ``CRX_DISABLE_NAVBAR`` and ``CRX_DISABLE_FOOTER`` settings. This way the logo and various other features remain customizable by the user. The Layout Settings, and the logo in particular, are used **extensively** throughout CRX and therefore may cause unexpected breakage if disabled. Defaults to ``False``.
+
+
+CRX_DISABLE_NAVBAR
+------------------
+
+If set to ``True``, the built-in ``Navbar`` model and related settings will be hidden from the Wagtail Admin.
+
+This is recommended for most sites that want to build their own more advanced navbar.
+
+
+CRX_DISABLE_FOOTER
+------------------
+
+If set to ``True``, the built-in ``Footer`` model and related settings will be hidden from the Wagtail Admin.
+
+This is recommended for most sites that want to build their own more advanced footer.
+
+
 CRX_FRONTEND_*
 --------------