diff --git a/README.md b/README.md index ad87d3e02..2e4eadb85 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,7 @@ To contribute, first read [How to Contirbute][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) + [Camino]: https://github.com/TACC/Camino diff --git a/package-lock.json b/package-lock.json index a0472cd77..276eaf0c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "minimist": "^1.2.6" }, "devDependencies": { - "@tacc/core-styles": "^2.45.0" + "@tacc/core-styles": "^2.46.2" }, "engines": { "node": ">=16", @@ -1132,9 +1132,9 @@ } }, "node_modules/@tacc/core-styles": { - "version": "2.45.0", - "resolved": "https://registry.npmjs.org/@tacc/core-styles/-/core-styles-2.45.0.tgz", - "integrity": "sha512-ujck0rgUFb+vo42Qr5MRUfF8KHnIAEjHsRuh9dhBaeo6mrRhyjBCq+JgGryjQxOiCnFFM8yuYrJlUPsCaSEGCA==", + "version": "2.46.2", + "resolved": "https://registry.npmjs.org/@tacc/core-styles/-/core-styles-2.46.2.tgz", + "integrity": "sha512-b3MTccuLswbJM19ysX85vHYt6l/hHtvEgR3ZhKZZvoVXPBZjyfhl9zvnOrnQPB5tnLMt59nKdyHo+XcEj67lgA==", "dev": true, "license": "MIT", "bin": { @@ -4733,9 +4733,9 @@ } }, "@tacc/core-styles": { - "version": "2.45.0", - "resolved": "https://registry.npmjs.org/@tacc/core-styles/-/core-styles-2.45.0.tgz", - "integrity": "sha512-ujck0rgUFb+vo42Qr5MRUfF8KHnIAEjHsRuh9dhBaeo6mrRhyjBCq+JgGryjQxOiCnFFM8yuYrJlUPsCaSEGCA==", + "version": "2.46.2", + "resolved": "https://registry.npmjs.org/@tacc/core-styles/-/core-styles-2.46.2.tgz", + "integrity": "sha512-b3MTccuLswbJM19ysX85vHYt6l/hHtvEgR3ZhKZZvoVXPBZjyfhl9zvnOrnQPB5tnLMt59nKdyHo+XcEj67lgA==", "dev": true, "requires": {} }, diff --git a/package.json b/package.json index efa0c1242..23b32a020 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,6 @@ }, "homepage": "https://github.com/TACC/Core-CMS", "devDependencies": { - "@tacc/core-styles": "^2.45.0" + "@tacc/core-styles": "^2.46.2" } } diff --git a/poetry.lock b/poetry.lock index 4fd2870f6..9d86b1964 100644 --- a/poetry.lock +++ b/poetry.lock @@ -747,13 +747,15 @@ taggit-helpers = ["django-taggit-helpers"] [[package]] name = "djangocms-bootstrap4" -version = "3.0.1" +version = "3.0.2" description = "Adds Bootstrap 4 components as plugins." optional = false python-versions = "*" groups = ["main"] -files = [] -develop = false +files = [ + {file = "djangocms_bootstrap4-3.0.2-py3-none-any.whl", hash = "sha256:3b7e7eb1b7551cf433bf2bb0f3601d47645203c3bb4ad75ec097eb4bd81a34cf"}, + {file = "djangocms_bootstrap4-3.0.2.tar.gz", hash = "sha256:e931aefadbd22ab00e6cea8b7224da767afea125df15bf0c8a8d5f7872153e64"}, +] [package.dependencies] django-cms = ">=3.7,<4" @@ -767,12 +769,6 @@ djangocms-text-ckeditor = ">=3.1.0" [package.extras] static-ace = ["djangocms-static-ace"] -[package.source] -type = "git" -url = "https://github.com/django-cms/djangocms-bootstrap4.git" -reference = "49983f4" -resolved_reference = "49983f4175ec4a4e2b5076993a893cbdd79c4ab2" - [[package]] name = "djangocms-column" version = "2.0.0" @@ -2502,4 +2498,4 @@ testing = ["func-timeout", "jaraco.itertools"] [metadata] lock-version = "2.1" python-versions = ">=3.11,<4.0" -content-hash = "077910c85e2fda3f3ff38d22d4275d876d2ecef54cd3e21ed2ee744c56771495" +content-hash = "8ef7c041a1522e5e00995677cce04b851ebfd5852399795d29aa902b49526d21" diff --git a/pyproject.toml b/pyproject.toml index 7a00eb064..a686634eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "tacc-core-cms-backend" -version = "4.35.0" +version = "4.36.3" description = "DjangoCMS backend for the TACC ACI-WMA Core-CMS Codebase." authors = ["TACC-WMA "] @@ -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" } diff --git a/taccsite_cms/apps.py b/taccsite_cms/apps.py index f3bdc022a..64d8e4bde 100644 --- a/taccsite_cms/apps.py +++ b/taccsite_cms/apps.py @@ -1,7 +1,10 @@ import logging + from django.apps import AppConfig from django.db.models.signals import post_migrate +from .djangocms_picture.extend import extendPicturePlugin + logger = logging.getLogger(f"portal.{__name__}") class TaccsiteCmsConfig(AppConfig): @@ -10,6 +13,7 @@ class TaccsiteCmsConfig(AppConfig): def ready(self): post_migrate.connect(self.create_groups, sender=self) + extendPicturePlugin() def create_groups(self, sender, **kwargs): from django.contrib.auth.models import Group diff --git a/taccsite_cms/contrib/bootstrap4_djangocms_picture/cms_plugins.py b/taccsite_cms/contrib/bootstrap4_djangocms_picture/cms_plugins.py deleted file mode 100644 index 3edf41136..000000000 --- a/taccsite_cms/contrib/bootstrap4_djangocms_picture/cms_plugins.py +++ /dev/null @@ -1,23 +0,0 @@ -# To support generic Image plugin without uninstalling Bootstrap's -# FAQ: Bootstrap Image plugin has features not desirable in TACC plugins -# FAQ: We must not break sites that already use Bootstrap Image plugin -try: - from django.utils.translation import gettext as _ - - from cms.plugin_pool import plugin_pool - - from djangocms_picture.cms_plugins import PicturePlugin - from djangocms_bootstrap4.contrib.bootstrap4_picture.cms_plugins import Bootstrap4PicturePlugin - - # To clairfy for users how this plugin differs from Generic > Image - Bootstrap4PicturePlugin.name = _('Picture / Image (Responsive)') - - # To re-register generic Picture plugin - # SEE: https://github.com/django-cms/djangocms-bootstrap4/blob/master/djangocms_bootstrap4/contrib/bootstrap4_picture/cms_plugins.py#L54 - plugin_pool.register_plugin(PicturePlugin) - -# To avoid server crash if Boostrap plugin is not registered -# CAVEAT: If import statement fails for reason other than Bootstrap presence, -# then that failure, and the failure of this plugin, is silent -except ImportError: - pass diff --git a/taccsite_cms/djangocms_picture/extend.py b/taccsite_cms/djangocms_picture/extend.py new file mode 100644 index 000000000..21798b481 --- /dev/null +++ b/taccsite_cms/djangocms_picture/extend.py @@ -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) diff --git a/taccsite_cms/management/commands/README.md b/taccsite_cms/management/commands/README.md index d2416238d..e28094462 100644 --- a/taccsite_cms/management/commands/README.md +++ b/taccsite_cms/management/commands/README.md @@ -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) @@ -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 [ ...] +``` + +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] diff --git a/taccsite_cms/management/commands/list_plugin_pages.py b/taccsite_cms/management/commands/list_plugin_pages.py new file mode 100644 index 000000000..8ae1b244d --- /dev/null +++ b/taccsite_cms/management/commands/list_plugin_pages.py @@ -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 diff --git a/taccsite_cms/settings.py b/taccsite_cms/settings.py index ed6927309..ba9a3ecc6 100644 --- a/taccsite_cms/settings.py +++ b/taccsite_cms/settings.py @@ -349,8 +349,10 @@ def gettext(s): return s # FAQ: List custom directory first, so custom templates take precedence # SEE: https://docs.djangoproject.com/en/2.2/topics/templates/#configuration 'DIRS': glob( + # XXX: Strange and from my ignorant implementation os.path.join(BASE_DIR, 'taccsite_custom') ) + [ + os.path.join(BASE_DIR, 'taccsite_custom', 'templates'), os.path.join(BASE_DIR, 'taccsite_cms', 'templates') ], 'OPTIONS': { @@ -481,7 +483,6 @@ def gettext(s): return s # django CMS Bootstrap # IDEA: Extend Bootstrap apps instead of overwrite 'taccsite_cms.contrib.bootstrap4_djangocms_link', - 'taccsite_cms.contrib.bootstrap4_djangocms_picture', # TACC CMS Plugins 'djangocms_tacc_image_gallery', @@ -523,9 +524,9 @@ def get_subdirs_as_module_names(path): # Append CMS project paths as module names to INSTALLED_APPS # FAQ: This automatically looks into `/taccsite_custom` and creates an "App" for each directory within CUSTOM_CMS_DIR = os.path.join(BASE_DIR, 'taccsite_custom') - INSTALLED_APPS_APPEND = get_subdirs_as_module_names(CUSTOM_CMS_DIR) -INSTALLED_APPS = INSTALLED_APPS + INSTALLED_APPS_APPEND +CORE_CMS_INDEX = INSTALLED_APPS.index('taccsite_cms') +INSTALLED_APPS[CORE_CMS_INDEX:CORE_CMS_INDEX] = INSTALLED_APPS_APPEND MIGRATION_MODULES = {} LANGUAGE_CODE = 'en' @@ -580,7 +581,9 @@ def get_subdirs_as_module_names(path): ('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"')), ] # FILE UPLOAD VALUES MUST BE SET! diff --git a/taccsite_cms/static/site_cms/css/src/_imports/components/django.cms.blog.app.page.css b/taccsite_cms/static/site_cms/css/src/_imports/components/django.cms.blog.app.page.css index 4a6c5ef4f..13dadc8da 100644 --- a/taccsite_cms/static/site_cms/css/src/_imports/components/django.cms.blog.app.page.css +++ b/taccsite_cms/static/site_cms/css/src/_imports/components/django.cms.blog.app.page.css @@ -170,6 +170,15 @@ Styleguide Components.DjangoCMS.Blog.App.Page margin-inline: auto; } +/* To add space under aligned images */ +/* NOTE: All images should be aligned by news editor, + so missing space is considered news editor error */ +:--article-page .blog-content .align-right, +:--article-page .blog-content .align-center, +:--article-page .blog-content .align-left { + margin-bottom: var(--global-space--grid-gap); +} + /* To always align Bootstrap blockquote text left */ /* FAQ: Boostrap, loaded in foundation layer, used !important */ /* TODO: When extending Core-Styles c-news, .blockquote... is only for CMS */ diff --git a/taccsite_cms/static/site_cms/css/test/djangocms-picture.css b/taccsite_cms/static/site_cms/css/test/djangocms-picture.css new file mode 100644 index 000000000..0a91e3675 --- /dev/null +++ b/taccsite_cms/static/site_cms/css/test/djangocms-picture.css @@ -0,0 +1,72 @@ +main { + .col { + /* To support notes */ + + :is(a, figure, span) { + position: relative; + } + :is(a, figure, span)::before { + position: absolute; + inset: 0 auto auto 0; + font-size: smaller; + background: rgb(from black r g b / 50%); + z-index: 1; + padding-inline: 0.5em; + } + + + /* To make notes unique */ + + a::before { + top: 0; + color: aqua; + content: ''; + } + a[class]::before { + content: ''; + } + a[data-class]::before { + content: ''; + } + + figure::before { + top: 3lh; + color: fuchsia; + content: '
'; + } + figure[class]::before { + content: '
'; + } + figure[data-class]::before { + content: '
'; + } + + span::before { + top: 6lh; + color: yellow; + content: ''; + } + + + /* To align notes to mimic tag hierarchy */ + + a > span::before, + a > figure::before, + figure > span::before { + margin-left: 1em; + } + a > figure > span::before { + margin-left: 2em; + } + + + /* To disable unsupported combos */ + + .u-image-zoom--on-hover:is( + span:not(a > *, a > figure > *), + figure:not(a > *) + ) { + opacity: 0.5; + } + } +} diff --git a/taccsite_cms/templates/djangocms_picture/default/picture.html b/taccsite_cms/templates/djangocms_picture/default/picture.html index 15f50ab31..463006bb3 100644 --- a/taccsite_cms/templates/djangocms_picture/default/picture.html +++ b/taccsite_cms/templates/djangocms_picture/default/picture.html @@ -1,45 +1,48 @@ {# https://github.com/django-cms/djangocms-picture/blob/3.0.0/djangocms_picture/templates/djangocms_picture/default/picture.html #} -{# TACC (mimic v3.0.0 to v4.0.0 changes): #} {# TACC (support children as caption content): #} +{# TACC (mimic v3.0.0 to v4.0.0 changes): #} +{# TACC (support image zoom): #} {# {% load thumbnail %} #} -{% load thumbnail l10n cms_tags %} +{% load thumbnail l10n cms_tags strip_class_attribute %} +{# /TACC #} {# /TACC #} {# /TACC #} -{# TACC (allow link to be conditional): #} -{% block picture_link_start %} +{# TACC (support custom condition): #} +{% if should_render_link %} {# /TACC #} -{% if picture_link %} - {# TACC (allow link to be conditional): #} - {% block picture_link %} - {# /TACC #} -{# TACC (allow link to be conditional): #} -{% endblock %} -{# /TACC #} + {# TACC (support image zoom): #} + class=" + {{ instance.attributes.class|default:'' }} + {{ instance.link_attributes.class|default:'' }} + {{ className }} + {% if should_add_zoom_class_to_link %}u-image-zoom--on-hover{% endif %} + " + {# TACC (assign attributes to parent): #} + {{ instance.attributes_str|strip_class_attribute|safe }} + {{ instance.link_attributes_str|strip_class_attribute|safe }} + {# /TACC #} + {# /TACC #} + > {% endif %} -{# TACC (allow link to be conditional): #} -{% endblock %} -{# /TACC #} -{# start render figure/figcaption #} + {# TACC (support children as caption content): #} -{# {% if instance.caption_text %} #} -{% if instance.caption_text or instance.child_plugin_instances %} +{% if has_figure_content %} +{# /TACC #} {# TACC (assign attributes to parent): #} - {#
#}
{# /TACC #} {% endif %} -{# /TACC #} -{# end render figure/figcaption #} +{# TACC (support image zoom): #} +{% if should_wrap_image_for_zoom %} + +{% endif %} +{# /TACC #} {# TACC (mimic v3.0.0 to v4.0.0 changes): #} {% localize off %} {# /TACC #} @@ -64,7 +67,7 @@ {# TACC (allow link to be conditional): #} {% block picture_attributes %} {# TACC (assign attributes to parent): #} - {% if not instance.caption_text and not picture_link and not instance.child_plugin_instances %} + {% if should_add_attributes_to_image %} {{ instance.attributes_str }} {% endif %} {# /TACC #} @@ -74,10 +77,15 @@ {# TACC (mimic v3.0.0 to v4.0.0 changes): #} {% endlocalize %} {# /TACC #} +{# TACC (support image zoom): #} + {% if should_wrap_image_for_zoom %} + + {% endif %} +{# /TACC #} -{# start render figure/figcaption #} -{# {% if instance.caption_text %} #} -{% if instance.caption_text or instance.child_plugin_instances %} +{# TACC (support custom condition): #} +{% if has_figure_content %} +{# /TACC #} {# TACC (support children as caption content): #} {#
{{ instance.caption_text }}
#}
@@ -89,17 +97,12 @@ {# /TACC #}
{% endif %} -{# end render figure/figcaption #} -{# TACC (allow link to be conditional): #} -{% block picture_link_end %} +{# TACC (support custom condition): #} +{% if should_render_link %} {# /TACC #} -{% if picture_link %}
{% endif %} -{# TACC (allow link to be conditional): #} -{% endblock %} -{# /TACC #} {% comment %} # https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img diff --git a/taccsite_cms/templates/djangocms_picture/no_link_to_ext_image/picture.html b/taccsite_cms/templates/djangocms_picture/no_link_to_ext_image/picture.html index 479af2ce9..d9e697407 100644 --- a/taccsite_cms/templates/djangocms_picture/no_link_to_ext_image/picture.html +++ b/taccsite_cms/templates/djangocms_picture/no_link_to_ext_image/picture.html @@ -1,28 +1,6 @@ {% extends "djangocms_picture/default/picture.html" %} -{# Do not let instance.external_picture trigger picture_link templating #} -{% comment %} -App djangocms_picture assumes external image must be linked to, -but we want to allow external image to be displayed without link. -{% endcomment %} -{# FAQ: picture_link is instance link_url or link_page_id or external_picture #} -{# https://github.com/django-cms/djangocms-picture/blob/3.0.0/djangocms_picture/models.py#L269-L276 #} -{# So picture with external image is not wrapped in a link #} -{% block picture_link_start %} - {% if instance.link_url or instance.link_page_id %} - {% block picture_link %} - {{ block.super }} - {% endblock %} - {% endif %} -{% endblock %} - -{# So picture attributes can be added to picture with external image #} -{% block picture_attributes %} - {% if not instance.caption_text and not instance.link_url and not instance.link_page_id and not instance.child_plugin_instances %} - {{ instance.attributes_str }} - {% endif %} -{% endblock %} - -{# So picture with external image is not wrapped in a link #} -{% block picture_link_end %} -{% endblock %} +{# To allow "External image" to be rendered without a link #} +{# FAQ: Template exists to avoid adding a new model field #} +{# FAQ: Default template uses custom boolean context variables for logic #} +{# FAQ: App djangocms_picture assumes that an image, if not explicitely linked, must link to itself. But sometimes, we want an image to just be an image, unlinked. #} diff --git a/taccsite_cms/templates/djangocms_picture/no_link_to_image/picture.html b/taccsite_cms/templates/djangocms_picture/no_link_to_image/picture.html new file mode 100644 index 000000000..5ab79b9f9 --- /dev/null +++ b/taccsite_cms/templates/djangocms_picture/no_link_to_image/picture.html @@ -0,0 +1,2 @@ +{# backwards-compatibility for temporary name #} +{% extends "djangocms_picture/no_link_to_ext_image/picture.html" %} diff --git a/taccsite_cms/templates/djangocms_picture/zoom_effect/picture.html b/taccsite_cms/templates/djangocms_picture/zoom_effect/picture.html new file mode 100644 index 000000000..f1f398d5c --- /dev/null +++ b/taccsite_cms/templates/djangocms_picture/zoom_effect/picture.html @@ -0,0 +1,5 @@ +{% extends "djangocms_picture/default/picture.html" %} + +{# To zoom image on hover #} +{# FAQ: Template exists to avoid adding a new model field #} +{# FAQ: Default template uses custom boolean context variables for logic #} diff --git a/taccsite_cms/templates/djangocms_picture/zoom_effect_no_link_to_ext_image/picture.html b/taccsite_cms/templates/djangocms_picture/zoom_effect_no_link_to_ext_image/picture.html new file mode 100644 index 000000000..e94d37638 --- /dev/null +++ b/taccsite_cms/templates/djangocms_picture/zoom_effect_no_link_to_ext_image/picture.html @@ -0,0 +1,5 @@ +{% extends "djangocms_picture/default/picture.html" %} + +{# To combine `zoom_effect` and `no_link_to_image` %} +{# SEE: taccsite_cms/templates/djangocms_picture/zoom_effect #} +{# SEE: taccsite_cms/templates/djangocms_picture/no_link_to_image #} diff --git a/taccsite_cms/templates/snippets/news/open-external-articles.html b/taccsite_cms/templates/snippets/news/open-external-articles.html index 99dbafa6d..ca383e8eb 100644 --- a/taccsite_cms/templates/snippets/news/open-external-articles.html +++ b/taccsite_cms/templates/snippets/news/open-external-articles.html @@ -1,7 +1,7 @@ {# To open external news article, instead of internal news article #} {% load static %} - diff --git a/taccsite_cms/templatetags/strip_class_attribute.py b/taccsite_cms/templatetags/strip_class_attribute.py new file mode 100644 index 000000000..24a55b428 --- /dev/null +++ b/taccsite_cms/templatetags/strip_class_attribute.py @@ -0,0 +1,22 @@ +from django import template +import re + +register = template.Library() + +@register.filter +def strip_class_attribute(attributes_str): + """ + Remove class attribute from an HTML attributes string. + Usage: {{ instance.attributes_str|strip_class_attribute|safe }} + """ + if not attributes_str: + return "" + + # To remove `class="…"` or anything equivalent + pattern = r'(? [!NOTE] -> This directory is a vestige of archived [Core CMS Resources](https://github.com/TACC/Core-CMS-Resources/tree/151ef91f) and we may re-evaluate whether to continue to use it. +> This directory is a vestige of archived [Core CMS Resources](https://github.com/TACC/Core-CMS-Resources/tree/151ef91f).