diff --git a/djangocms_tacc_header_logo/__init__.py b/djangocms_tacc_header_logo/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/djangocms_tacc_header_logo/apps.py b/djangocms_tacc_header_logo/apps.py new file mode 100644 index 000000000..803ac60b5 --- /dev/null +++ b/djangocms_tacc_header_logo/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class TaccsiteHeaderLogoConfig(AppConfig): + name = 'djangocms_tacc_header_logo' + verbose_name = 'Header logo' diff --git a/djangocms_tacc_header_logo/cms_plugins.py b/djangocms_tacc_header_logo/cms_plugins.py new file mode 100644 index 000000000..b10afd0fe --- /dev/null +++ b/djangocms_tacc_header_logo/cms_plugins.py @@ -0,0 +1,31 @@ +from cms.plugin_pool import plugin_pool +from django.utils.translation import gettext_lazy as _ + +from djangocms_picture.cms_plugins import PicturePlugin +from djangocms_picture.models import Picture + +from .forms import TaccsiteHeaderLogoForm + +HEADER_LOGO_ID = 'header-logo' +HEADER_LOGO_CLASS = 'navbar-brand' + + +@plugin_pool.register_plugin +class TaccsiteHeaderLogoPlugin(PicturePlugin): + """ + Components > "Header logo" Plugin + """ + model = Picture + form = TaccsiteHeaderLogoForm + module = 'TACC Site' + name = _('Header logo') + + def render(self, context, instance, placeholder): + if not instance.attributes: + instance.attributes = {} + + existing_class = instance.attributes.get('class', '') + instance.attributes['class'] = f'{HEADER_LOGO_CLASS} {existing_class}'.strip() + instance.attributes['id'] = HEADER_LOGO_ID + + return super().render(context, instance, placeholder) diff --git a/djangocms_tacc_header_logo/forms.py b/djangocms_tacc_header_logo/forms.py new file mode 100644 index 000000000..6e0cd6dcc --- /dev/null +++ b/djangocms_tacc_header_logo/forms.py @@ -0,0 +1,38 @@ +from cms.models import Page +from django.conf import settings +from django.contrib.sites.models import Site +from djangocms_picture.forms import PictureForm + +HEADER_LOGO_TEMPLATE_WITH_PORTAL = 'portal_logo' + + +def default_home_page(): + site = Site.objects.get_current() + return Page.objects.filter(is_home=True, node__site=site).first() + + +class TaccsiteHeaderLogoForm(PictureForm): + """ + Defaults for new Header logo plugins only (admin add form). + + On a TACC/Core-Portal instance, defaults to Portal logo picture template. + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if self.instance.pk: + return + + if settings.PORTAL_IS_TACC_CORE_PORTAL: + self.initial.setdefault('template', HEADER_LOGO_TEMPLATE_WITH_PORTAL) + + home = default_home_page() + if home is not None: + self.initial.setdefault('link_page', home.pk) + + self.initial.setdefault('use_no_cropping', True) + self.initial.setdefault('use_automatic_scaling', False) + + attributes = dict(self.initial.get('attributes') or {}) + attributes.setdefault('alt', 'Project logo') + self.initial['attributes'] = attributes diff --git a/docs/gh-999-editable-header-plan.md b/docs/gh-999-editable-header-plan.md new file mode 100644 index 000000000..dd0adfe8d --- /dev/null +++ b/docs/gh-999-editable-header-plan.md @@ -0,0 +1,67 @@ +# GH-999: Editable Header + +Related: + +- [#999](https://github.com/TACC/Core-CMS/issues/999) +- [#1083](https://github.com/TACC/Core-CMS/pull/1083) + +## Status as of 2026-06 + +| State | Description | +| ------------------- | --------------------------------- | +| one placeholder | `header-logo` static placeholder | +| if plugin is added | `TaccsiteHeaderLogoPlugin` is only option | +| if plugin not added | renders settings-based logo | + +The branding and navs and search are still settings-based. + +## Known Limitations + +| Issue | Summary | Why Allow This? | +| --- | --- | --- | +| [#1185](https://github.com/TACC/Core-CMS/issues/1185) | Header logo without a link is **missing** `class="portal-logo"` on ``. | Default form links logo to home, so typical sites are unaffected. | + +## How to Add Other Header Plugins + +Requirements: + +- (for [CMS editor UX](#cms-editor-ux)) plugins render where `{% static_placeholder %}` sits +- (to skip [Failed Ideas](#failed-ideas)) pick one option from below + +### A. Wrapper Plugin + +| Pros | Cons | +| ------------------------------ | ---------------------------------------------- | +| One placeholder | Extra container plugin | +| Layout management consolidated | Build/Maintain wrapper | +| | Must deprecate or migrate legacy `header-logo` | + +### B. Multiple Placeholders + +| Pros | Cons | +| ---------------------------------- | ---------------------------------- | +| Typical CMS usage | Several placeholders to configure | +| | Layout management not consolidated | +| Can keep using `header-logo` as is | | + +## CMS Editor UX + +Requirements: + +- Static placeholder always visible in Structure mode. +- Edit plugin: auto-refreshes page preview. +- Add/Delete plugin: auto-refreshes page preview. +- Structure mode button toggles render of Structure sidebar. + +## Failed Ideas + +| Idea | Problem | +| --- | --- | +| `ContentRenderer` / template tag for the logo instead of `{% static_placeholder %}` at the logo | Structure and preview break; Picture edits need refresh (removed in [#1189](https://github.com/TACC/Core-CMS/pull/1189)) | +| Published markup from code, placeholder somewhere else | Duplicate logo surfaces; draft image lags behind preview | +| Child plugins under the logo, HTML rendered elsewhere | Structure/preview break | +| `{% static_placeholder %}` not where the logo shows | Bad WYSIWYG; toolbar/Structure bugs | +| Per-region render registry (not Option A or B) | Live site publish-only; bad edit UX | +| Option A wrapper before more header plugins exist | Two plugins for one logo | +| Plugin `cache = False` only | Does not fix placeholder vs programmatic render | +| Edit-only placeholder at top of header | Structure JS errors; no real preview | diff --git a/taccsite_cms/_settings/djangocms_plugins.py b/taccsite_cms/_settings/djangocms_plugins.py index 87e378fd8..48f1b3380 100644 --- a/taccsite_cms/_settings/djangocms_plugins.py +++ b/taccsite_cms/_settings/djangocms_plugins.py @@ -47,6 +47,7 @@ ] DJANGOCMS_PICTURE_TEMPLATES = [ ('no_link_to_ext_image', _('Do not link to external image')), + ('portal_logo', _('Portal logo')), ] ######################## diff --git a/taccsite_cms/settings/settings.py b/taccsite_cms/settings/settings.py index cc1eed190..1e3633e39 100644 --- a/taccsite_cms/settings/settings.py +++ b/taccsite_cms/settings/settings.py @@ -494,6 +494,7 @@ def gettext(s): return s 'taccsite_card', 'djangocms_tacc_image_gallery', 'djangocms_tacc_system_monitor', + 'djangocms_tacc_header_logo', # TACC CMS Plugins - DECPRECATED 'taccsite_cms.contrib.taccsite_blockquote', @@ -567,6 +568,17 @@ def get_subdirs_as_module_names(path): CMS_PERMISSION = True CMS_PLACEHOLDER_CONF = { + None: { + 'excluded_plugins': ['TaccsiteHeaderLogoPlugin'], + }, + 'header-logo': { + 'name': _('Header logo'), + 'plugins': ['TaccsiteHeaderLogoPlugin'], + 'excluded_plugins': [], + 'limits': { + 'global': 1, + }, + }, 'footer-content': { 'name': _('Footer content'), }, diff --git a/taccsite_cms/templates/djangocms_picture/default/picture.html b/taccsite_cms/templates/djangocms_picture/default/picture.html index 160e312ff..0ec26a477 100644 --- a/taccsite_cms/templates/djangocms_picture/default/picture.html +++ b/taccsite_cms/templates/djangocms_picture/default/picture.html @@ -59,6 +59,9 @@ {{ instance.attributes_str }} {% endblock %} {# /TACC #} + {# TACC (allow attributes to be force-added to ): #} + {% block picture_attributes_img %}{% if picture_img_class %}class="{{ picture_img_class }}"{% endif %}{% endblock %} + {# /TACC #} > {% endlocalize %} diff --git a/taccsite_cms/templates/djangocms_picture/portal_logo/picture.html b/taccsite_cms/templates/djangocms_picture/portal_logo/picture.html new file mode 100644 index 000000000..03e6af734 --- /dev/null +++ b/taccsite_cms/templates/djangocms_picture/portal_logo/picture.html @@ -0,0 +1,2 @@ +{% extends "djangocms_picture/default/picture.html" %} +{% block picture_attributes_img %}class="portal-logo"{% endblock %} diff --git a/taccsite_cms/templates/header.html b/taccsite_cms/templates/header.html index 6eaa2ce2a..b6e3880b4 100644 --- a/taccsite_cms/templates/header.html +++ b/taccsite_cms/templates/header.html @@ -1,4 +1,5 @@ {# @var settings #} +{% load cms_tags %} {# WARNING: Some markup is duplicated in other repositories #} {# SEE: https://confluence.tacc.utexas.edu/x/LoCnCQ #} @@ -22,7 +23,9 @@ {% endif %} "> - {% include "header_logo.html" %} + {% static_placeholder "header-logo" or %} + {% include "header_logo.html" %} + {% endstatic_placeholder %}