Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
3219e53
feat: support multisite
wesleyboar Sep 13, 2024
85b349e
Merge branch 'main' into feat/support-multisite
wesleyboar Sep 13, 2024
817c81d
Merge branch 'main' into feat/support-multisite
wesleyboar May 15, 2025
eeb4df8
refactor: simplify and rename middleware
wesleyboar May 15, 2025
5b1b176
Merge branch 'main' into feat/support-multisite
wesleyboar May 27, 2025
3a1e28e
fix: inaccurate text picture template name (#967)
wesleyboar Jul 21, 2025
893a434
refactor: no_link_to_image
wesleyboar Jul 22, 2025
91cc70c
docs: simplify no_link_to_image/picture.html notes
wesleyboar Jul 22, 2025
8cbb4a9
docs: clarify no_link_to_image/picture.html notes
wesleyboar Jul 22, 2025
6d7b730
docs: djangocms_picture template indentation
wesleyboar Jul 22, 2025
7f3ec21
feat: support core-styles u-image-zoom (#968)
wesleyboar Jul 25, 2025
5ec5e02
refactor!: djangocms_picture extension (#969)
wesleyboar Jul 26, 2025
478ffb5
chore(README): new line (to get new main commit)
wesleyboar Jul 26, 2025
de1eacf
Merge branch 'main' of github.com:TACC/Core-CMS
wesleyboar Jul 26, 2025
df14ad6
refactor: u-image-zoom via core-styles (#970)
wesleyboar Jul 28, 2025
461ca3e
chore: v4.36.0
wesleyboar Jul 28, 2025
5c1886c
Merge branch 'main' of github.com:TACC/Core-CMS
wesleyboar Jul 28, 2025
34acc58
fix: u-image-zoom validation (#971)
wesleyboar Jul 28, 2025
b2993c0
fix: space under span.u-image-zoom img (#972)
wesleyboar Jul 28, 2025
86f76fb
fix: v4.36.1
wesleyboar Jul 28, 2025
9d80485
feat: snippet template to redirect external article (#973)
wesleyboar Jul 29, 2025
6913b38
chore: v4.36.2
wesleyboar Jul 29, 2025
04a54de
deps: djangocms-bootstrap4 v3 (#974)
wesleyboar Aug 4, 2025
ab22440
chore: move imports to function that uses them
wesleyboar Aug 4, 2025
4b72436
feat: let custom sites overwrite app templates (#975)
wesleyboar Aug 5, 2025
69dcb55
chore: v4.36.3
wesleyboar Aug 15, 2025
4a457ca
feat: new custom django command for devs (#728)
wesleyboar Sep 4, 2025
6661622
docs: we do not use Core-CMS-Resources
wesleyboar Sep 5, 2025
bfcca6a
feat: let client have fullwidth breadcrumbs (#985)
wesleyboar Sep 8, 2025
8904111
docs: revise note about code from 3rd-party
wesleyboar Sep 8, 2025
723c182
fix: client cannot change breadcrumb inclusion on guides (#986)
wesleyboar Sep 8, 2025
9d1cff2
fix: do not allow accidental blog install (#987)
wesleyboar Sep 12, 2025
2c6896a
feat: progressive web app & full favicon (#988) (#989)
wesleyboar Sep 13, 2025
5136026
feat: PORTAL_LOGIN_PATH (#990) (#991)
wesleyboar Sep 30, 2025
14611bf
chore: v4.36.4
wesleyboar Sep 30, 2025
8494266
fix: big error if core portal expected but absent (#993) (#994)
wesleyboar Oct 1, 2025
f8a59ca
docs: move docs/contributing to CONTRIBUTING
wesleyboar Oct 1, 2025
7b24129
chore: v4.36.5
wesleyboar Oct 1, 2025
b7a8e67
chore: poetry lock
wesleyboar Oct 1, 2025
7badb77
fix: djangocms-icon picker not showing Cortal icons (#995)
wesleyboar Oct 2, 2025
8f286c3
docs: icon picker bug
wesleyboar Oct 2, 2025
8524bb4
chore: v4.36.6
wesleyboar Oct 2, 2025
80208ac
fix: svg could display content (#997)
wesleyboar Oct 3, 2025
f1d8b3a
refactor: from portal.png to logo.svg
wesleyboar Oct 6, 2025
3797eed
Merge branch 'refactor/portal-png-to-logo-svg'
wesleyboar Oct 6, 2025
94f2356
refactor: from portal.png to logo.svg (#1001)
wesleyboar Oct 6, 2025
0a9bada
Merge branch 'stable'
wesleyboar Oct 13, 2025
0b24eb3
refactor: no need to annotate tags (#1006)
wesleyboar Oct 14, 2025
c90889a
chore: v4.35.7
wesleyboar Oct 15, 2025
36573bd
Merge branch 'stable'
wesleyboar Oct 15, 2025
c8eb94f
fix: image caption breaks alignment (old styles) (#1009)
wesleyboar Oct 16, 2025
4e9b795
feat: serve page with no header nor footer (#1012) (#1013)
wesleyboar Oct 24, 2025
594f31f
fix: serve page with no header nor footer (#1014) (#1015)
wesleyboar Oct 24, 2025
d620fee
feat: always allow CMS_TEMPLATE content.html (#1017)
wesleyboar Oct 24, 2025
445e457
refactor: DJANGOCMS plugins settings (off main) (#1019)
wesleyboar Oct 29, 2025
0dd7485
Merge branch 'stable'
wesleyboar Oct 30, 2025
339a440
chore: v4.36.10
wesleyboar Oct 30, 2025
3b52267
feat: support DjangoCMS Bootstrap4 Link/Button (#1020)
wesleyboar Nov 12, 2025
9e7b305
fix: core-styles v2.50.2 (#1021)
wesleyboar Nov 12, 2025
7eace56
chore: v4.36.11
wesleyboar Nov 12, 2025
70141ac
fix: link-wrapped img ignores box-model (#1023)
wesleyboar Nov 13, 2025
4ef206b
feat/new-email-svg (#1024)
wesleyboar Nov 13, 2025
82cbb84
fix: social media icons were giant by default (#1026)
wesleyboar Nov 13, 2025
97ccb58
refactor: simpler svg data for core logo
wesleyboar Nov 14, 2025
e6685c0
feat: logo icons inherit color & size (#1027)
wesleyboar Nov 14, 2025
f0dae34
chore v4.36.12
wesleyboar Nov 14, 2025
273bb04
feat: remove redundant social media icons - off main (#1031)
wesleyboar Nov 14, 2025
dd98597
chore v4.36.13
wesleyboar Nov 14, 2025
ef76b43
Merge branch 'stable'
wesleyboar Nov 19, 2025
a849392
chore: v4.36.14
wesleyboar Nov 19, 2025
63920a2
Merge branch 'main' into feat/support-multisite
wesleyboar Jan 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ To contribute, first read [How to Contribute][Contributing].
| `python manage.py collectstatic` | [django](https://docs.djangoproject.com/en/3.2/howto/static-files/)
| `python manage.py createsuperuser` | [django cms](https://docs.django-cms.org/en/release-3.8.x/how_to/install.html#admin-user), [django](https://docs.djangoproject.com/en/3.2/ref/django-admin/#createsuperuser)


<!-- Link Aliases -->

[Camino]: https://github.com/TACC/Camino
Expand Down
16 changes: 6 additions & 10 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 2 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "tacc-core-cms-backend"
version = "v4.35.14"
version = "v4.36.14"
description = "DjangoCMS backend for the TACC ACI-WMA Core-CMS Codebase."
authors = ["TACC-WMA <wma-portals@tacc.utexas.edu>"]

Expand Down Expand Up @@ -45,9 +45,7 @@ djangocms-admin-style = "~3.2.6"
djangocms-apphook-setup = "0.4.1"
djangocms-attributes-field = "2.1.0"
djangocms-blog = "^1.2"
# TO get a commit in main (since v3.0.1) to fix Container error
# https://github.com/django-cms/djangocms-bootstrap4/pull/164
djangocms-bootstrap4 = {git = "https://github.com/django-cms/djangocms-bootstrap4.git", rev = "49983f4"}
djangocms-bootstrap4 = "^3.0"
djangocms-column = "^2.0"
djangocms-file = "3.0.0"
djangocms-forms-maintained = { git = "https://github.com/TACC/djangocms-forms", rev = "6b59ff366495915f06f4d6fac01a2f0aa9efecaf" }
Expand Down
4 changes: 3 additions & 1 deletion taccsite_cms/_settings/djangocms_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@
('center', _('Align center')),
]
DJANGOCMS_PICTURE_TEMPLATES = [
('no_link_to_ext_image', _('Do not link to external image')),
('no_link_to_ext_image', _('Do not link to "External image"')),
('zoom_effect', _('Zoom image on hover')),
('zoom_effect_no_link_to_ext_image', _('Zoom image on hover & Do not link to "External image"')),
]

########################
Expand Down
3 changes: 3 additions & 0 deletions taccsite_cms/apps.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import logging

from django.apps import AppConfig
from django.db.models.signals import post_migrate

from .djangocms_picture.extend import extendPicturePlugin
from .djangocms_bootstrap4.contrib.bootstrap4_link.extend import extendBootstrap4LinkPlugin

logger = logging.getLogger(f"portal.{__name__}")
Expand All @@ -12,6 +14,7 @@ class TaccsiteCmsConfig(AppConfig):

def ready(self):
post_migrate.connect(self.create_groups, sender=self)
extendPicturePlugin()
extendBootstrap4LinkPlugin()

def create_groups(self, sender, **kwargs):
Expand Down
23 changes: 0 additions & 23 deletions taccsite_cms/contrib/bootstrap4_djangocms_picture/cms_plugins.py

This file was deleted.

4 changes: 3 additions & 1 deletion taccsite_cms/custom_app_settings.example.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@
'djangocms_blog',

]
CUSTOM_MIDDLEWARE = []
CUSTOM_MIDDLEWARE = [
'taccsite_cms.middleware.settings.DynamicSiteIdMiddleware'
]
STATICFILES_DIRS = ()
163 changes: 163 additions & 0 deletions taccsite_cms/djangocms_picture/extend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
def extendPicturePlugin():
from django.utils.translation import gettext_lazy as _

from cms.plugin_pool import plugin_pool

ZOOM_TEMPLATE_NAME = 'zoom_effect'
ZOOM_TEMPLATE_LABEL = 'Zoom image on hover'
ZOOM_TEMPLATE_NOTE = _('The "%(zoom_template_label)s" templates only have effect if Image either has a Link or is within a Link.') % {"zoom_template_label": ZOOM_TEMPLATE_LABEL}
ZOOM_TEMPLATE_ERROR = _(' "%(zoom_template_label)s" templates require Image to either have a Link or be within a Link.') % {"zoom_template_label": ZOOM_TEMPLATE_LABEL}

LINK_TEMPLATE_NAME = 'no_link_to_ext_image'

def add_help_text(form_instance):
"""Adds help text for: 'Template' field"""

if 'template' in form_instance.fields:
form_instance.fields['template'].help_text += _(' %(ZOOM_TEMPLATE_NOTE)s') % {"ZOOM_TEMPLATE_NOTE": ZOOM_TEMPLATE_NOTE}

def whether_to_render_link(instance):
has_explicit_link = bool(instance.link_url or instance.link_page_id)
# FAQ: The djangocms_picture has "feature" such that an image with
# "External image" URL will automatically link to that resource
has_implicit_link = bool(instance.get_link()) and not has_explicit_link

allow_implicit_link = not LINK_TEMPLATE_NAME in instance.template

if has_explicit_link or (has_implicit_link and allow_implicit_link):
return True

return False

def validate_zoom_template(instance):
"""Validates: 'Template' field choice 'Zoom image …'"""
from django.core.exceptions import ValidationError
from djangocms_link.cms_plugins import LinkPlugin

errors = {}

would_render_link = whether_to_render_link(instance)
should_add_zoom_effect = ZOOM_TEMPLATE_NAME in instance.template
parent_plugin = instance.parent.get_plugin_instance()[0] if instance.parent else None
is_in_link = instance.parent.plugin_type == 'LinkPlugin' if instance.parent else False

if (should_add_zoom_effect and not would_render_link and not is_in_link):
errors['template'] = ZOOM_TEMPLATE_ERROR

if errors:
raise ValidationError(errors)

def get_more_context_variables(instance):
"""
Calculate boolean context variables to simplify template logic.
Returns a dictionary of context variables.
"""
# Figure/Caption
has_caption_text = bool(instance.caption_text)
has_child_plugins = bool(instance.child_plugin_instances)
has_figure_content = has_caption_text or has_child_plugins

# Template
is_zoom_template = ZOOM_TEMPLATE_NAME in instance.template

# Link
should_render_link = whether_to_render_link(instance)

# Zoom Effect
should_add_zoom_effect = is_zoom_template
should_wrap_image_for_zoom = (
should_add_zoom_effect and
(has_figure_content or not should_render_link)
)
should_add_zoom_class_to_link = (
should_add_zoom_effect and
should_render_link and
not has_figure_content
)

# Attributes
should_add_attributes_to_image = (
not has_figure_content and
not should_render_link
)

return {
'should_render_link': should_render_link,
'has_figure_content': has_figure_content,
'should_wrap_image_for_zoom': should_wrap_image_for_zoom,
'should_add_zoom_class_to_link': should_add_zoom_class_to_link,
'should_add_attributes_to_image': should_add_attributes_to_image,
}


# djangocms_picture
from djangocms_picture.cms_plugins import PicturePlugin as OriginalPicturePlugin
from djangocms_picture.models import Picture as OriginalPicture

class PicturePluginForm(OriginalPicturePlugin.form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
add_help_text(self)

class PicturePluginModel(OriginalPicture):
class Meta:
proxy = True

def clean(self):
super().clean()
validate_zoom_template(self)

class PicturePlugin(OriginalPicturePlugin):
model = PicturePluginModel
form = PicturePluginForm
name = 'Image'

def render(self, context, instance, placeholder):
context = super().render(context, instance, placeholder)

more_context = get_more_context_variables(instance)
context.update(more_context)

return context

# To support generic Image plugin without uninstalling Bootstrap's
# FAQ: Had been done cuz Image plugin had percieved use cases,
# but is since not regularly used, but is used, thus maintained
# FAQ: No need to unregister cuz Bootstrap4PicturePlugin does that
# https://github.com/django-cms/djangocms-bootstrap4/blob/3.0.0/djangocms_bootstrap4/contrib/bootstrap4_picture/cms_plugins.py#L54
# plugin_pool.unregister_plugin(OriginalPicturePlugin)
plugin_pool.register_plugin(PicturePlugin)


# djangocms_bootstrap4: bootstrap4_picture
from djangocms_bootstrap4.contrib.bootstrap4_picture.cms_plugins import Bootstrap4PicturePlugin as OriginalBootstrap4PicturePlugin
from djangocms_bootstrap4.contrib.bootstrap4_picture.models import Bootstrap4Picture as OriginalBootstrap4Picture

class Bootstrap4PictureForm(OriginalBootstrap4PicturePlugin.form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
add_help_text(self)

class Bootstrap4PictureModel(OriginalBootstrap4Picture):
class Meta:
proxy = True

def clean(self):
super().clean()
validate_zoom_template(self)

class Bootstrap4PicturePlugin(OriginalBootstrap4PicturePlugin):
model = Bootstrap4PictureModel
form = Bootstrap4PictureForm
name = 'Picture / Image (Responsive)'

def render(self, context, instance, placeholder):
context = super().render(context, instance, placeholder)

more_context = get_more_context_variables(instance)
context.update(more_context)

return context

plugin_pool.unregister_plugin(OriginalBootstrap4PicturePlugin)
plugin_pool.register_plugin(Bootstrap4PicturePlugin)
16 changes: 16 additions & 0 deletions taccsite_cms/management/commands/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- [How to Use](#how-to-use)
- [List Pages Using Each Template](#list-pages-using-each-template)
- [List Pages Using Plugins](#list-pages-using-plugins)
- [Set Groups & Permissions](#set-groups--permissions)
- [Reference](#reference)

Expand All @@ -25,6 +26,21 @@ Usage:
python manage.py list_page_templates
```

## List Pages Using Plugins

This command lists all pages that use a specific plugin instance (given a plugin instance ID), along with the page's path.

Usage:
```sh
python manage.py list_plugin_pages <plugin_instance_id> [<plugin_instance_id> ...]
```

Example:
```sh
python manage.py list_plugin_pages 42 99
```


## Set Groups & Permissions

Every file in [`group_perms/`](./group_perms) represents a group. Each group's intended usage is described at the top of its file. Permissions are set via function calls in each file.[^1]
Expand Down
25 changes: 25 additions & 0 deletions taccsite_cms/management/commands/list_plugin_pages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from django.core.management import BaseCommand
from django.apps import apps

class Command(BaseCommand):
help = 'Finds the page that uses a specific plugin instance by its ID'

def add_arguments(self, parser):
parser.add_argument('plugin_instance_ids', nargs='+', type=str)

def handle(self, *args, **options):
for plugin_instance_id in options['plugin_instance_ids']:
page = self.find_page_using_plugin_instance(plugin_instance_id)
if page:
self.stdout.write(f'Plugin instance ID {plugin_instance_id} is used on page: {page.get_path()}')
else:
self.stdout.write(f'No page found using plugin instance ID {plugin_instance_id}')

def find_page_using_plugin_instance(self, plugin_instance_id):
try:
CMSPlugin = apps.get_model('cms', 'CMSPlugin')
plugin_instance = CMSPlugin.objects.get(id=plugin_instance_id)
page = plugin_instance.placeholder.page if plugin_instance.placeholder else None
return page
except CMSPlugin.DoesNotExist:
return None
18 changes: 18 additions & 0 deletions taccsite_cms/middleware/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""Change site based on domain name (fallback to default site)"""
from django.conf import settings
from django.contrib.sites.models import Site

class DynamicSiteIdMiddleware:
def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
try:
host = request.get_host()
site = Site.objects.get(domain=host)
settings.SITE_ID = site.id
except Site.DoesNotExist:
settings.SITE_ID = getattr(settings, 'DEFAULT_SITE_ID', 1)

response = self.get_response(request)
return response
Loading