Skip to content

Commit 7041140

Browse files
authored
fix: Draft static aliases disappeared when viewing pages on site (#297)
* fix: Draft static aliases disappeard when viewing pages on site * Bump version * Update readme * docs: Improve test docstring * fix: improve error message of migration * fix: Show alias name if not static code is available in migration error message * Update tests * Update readme * Clean up README formatting and remove blank lines Removed extra blank lines and adjusted formatting in the README. * fix: Update readme to explain template structure * fix typos * Shorten README * Add double-backticks * One more typo * Hierarchy of README sections * README refinement
1 parent c93410c commit 7041140

23 files changed

+535
-35
lines changed

CHANGELOG.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
Changelog
33
=========
44

5+
3.0.1 (2026-09-18)
6+
==================
7+
8+
* fix: Draft static aliases disappeared when viewing pages on site by @fsbraun in https://github.com/django-cms/djangocms-alias/pull/295
9+
* fix: Error message message referred to wrong package by @mbi in https://github.com/django-cms/djangocms-alias/pull/296
10+
511
3.0.0 (2026-09-17)
612
==================
713

README.rst

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
2-
31
****************
42
django CMS Alias
53
****************
@@ -58,6 +56,36 @@ Run::
5856
to perform the application's database migrations.
5957

6058

59+
=============
60+
Configuration
61+
=============
62+
63+
django CMS Alias provides several Django settings to control its behavior:
64+
65+
``STATIC_ALIAS_EDITING_ENABLED``
66+
Default: ``True``
67+
68+
Controls whether static aliases can be edited directly on frontend editable objects
69+
(such as pages) that include the ``{% static_alias %}`` template tag. When set to ``False``,
70+
static aliases will not be visible in the structure board and only editable from the alias
71+
admin endpoint.
72+
73+
``VERSIONING_ALIAS_MODELS_ENABLED``
74+
Default: ``True`` (if djangocms-versioning is installed)
75+
76+
Enables versioning support for alias models when djangocms-versioning is available. When enabled,
77+
aliases support draft/published workflows, version history, and proper content lifecycle management.
78+
Set to ``False`` to disable versioning for aliases even if djangocms-versioning is installed. Any changes
79+
to any alias will then be immediately visible to the world.
80+
81+
``MODERATING_ALIAS_MODELS_ENABLED``
82+
Default: ``True`` (if djangocms-moderation is installed)
83+
84+
Enables moderation workflows for alias models when djangocms-moderation is available. When enabled,
85+
aliases can be subject to approval workflows before publication. Set to ``False`` to disable moderation
86+
for aliases even if djangocms-moderation is installed.
87+
88+
6189
=====
6290
Usage
6391
=====
@@ -75,15 +103,62 @@ Example::
75103
{% static_alias 'footer' %}
76104
</footer>
77105

106+
**New in version 3**: Static aliases can now be edited directly on any frontend
107+
editable object (such as pages) that includes the ``{% static_alias %}``
108+
template tag. Static aliases are marked by a pin icon in the structure board to
109+
distinguish them from regular content.
110+
111+
Editing static aliases on the page provides a convenient way to manage alias
112+
content in context. However, when using djangocms-versioning, there are important
113+
considerations:
114+
115+
**Versioning Considerations:**
116+
117+
* **Independent Publishing**: Static aliases must be published independently from
118+
their edit endpoint. Use the edit entry in the alias's burger menu in the structure
119+
board to access the full alias editing interface.
120+
121+
* **Published Content Only**: When objects are viewed on the site (not in edit mode),
122+
only the latest published alias version is displayed. If no published version exists,
123+
nothing will be shown.
124+
125+
* **Draft Creation Required**: Published aliases cannot be edited - neither in the
126+
structure menu nor on their dedicated endpoint. You must create a new draft version
127+
of the alias before editing is possible.
128+
129+
This workflow ensures content consistency and proper version control while providing the flexibility to edit aliases in context when appropriate.
130+
78131
Alias plugin
79132
============
80133

81134
Alternatively, aliases can be used with the Alias plugin. It allows to select which alias content is shown at the
82135
exact position the alias plugin is placed.
83136

84-
Side notes
85-
==========
137+
=========
138+
Templates
139+
=========
86140
For the plugin to work out of the box ``{% block content %}`` is expected to exist in your main ``base.html`` file.
141+
Here is the template hierarchy for the edit and preview endpoints::
142+
143+
base.html
144+
└── djangocms_alias/base.html {% block content %}
145+
└── djangocms_alias/alias_content_preview.html {% block alias_content %}
146+
147+
Use Django's template override mechanism to customize these templates as needed. Say, if your base template has
148+
a different name and the content goes into a block called ``main_content``, you would create a template at
149+
``templates/djangocms_alias/base.html`` with the following content::
150+
{% extends "mybase.html" %}
151+
{% load i18n %}
152+
153+
{% block title %}{% translate "Aliases" %}{% endblock %}
154+
{% block main_content %}
155+
<div class="aliases my-additional-class">
156+
{% block aliases_content %}
157+
{% endblock aliases_content %}
158+
</div>
159+
{% endblock main_content %}
160+
161+
87162

88163
.. |PyPiVersion| image:: https://img.shields.io/pypi/v/djangocms-alias.svg?style=flat-square
89164
:target: https://pypi.python.org/pypi/djangocms-alias
@@ -101,4 +176,4 @@ For the plugin to work out of the box ``{% block content %}`` is expected to exi
101176

102177
.. |CmsVersion| image:: https://img.shields.io/pypi/frameworkversions/django-cms/djangocms-alias.svg?style=flat-square
103178
:target: https://pypi.python.org/pypi/djangocms-alias
104-
:alt: django CMS versions
179+
:alt: django CMS versions

djangocms_alias/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
A reference can be added to any placeholder using the Alias plugin.
44
"""
55

6-
__version__ = "3.0.0"
6+
__version__ = "3.0.1"

djangocms_alias/migrations/0005_dynamic_slot_names.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,12 @@ def migrate_slots(apps, schema_editor, forward=True):
3434
elif len(slots) > 1:
3535
print(
3636
f"AliasContent {alias_content.pk} has multiple placeholders, expected only one. "
37-
"Skipping migration for this instance."
37+
"Skipping migration for this instance:\n"
38+
f"{alias_content.alias.static_code or alias_content.name} "
39+
f"({alias_content.language}, pk={alias_content.pk})",
3840
)
41+
for placeholder in slots:
42+
print(f" - Placeholder {placeholder.pk} with slot '{placeholder.slot}'")
3943

4044

4145
class Migration(migrations.Migration):

djangocms_alias/rendering.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from django.template.response import TemplateResponse
1414
from django.utils.safestring import mark_safe
1515

16-
from djangocms_alias.templatetags.djangocms_alias_tags import StaticAlias
16+
from djangocms_alias.templatetags.djangocms_alias_tags import StaticAlias, _static_alias_editing_enabled
1717

1818
from .models import Alias, AliasContent
1919

@@ -28,6 +28,11 @@ def render_alias_content(request: HttpRequest, alias_content: str) -> TemplateRe
2828

2929

3030
def get_declared_static_aliases(template: str) -> list["DeclaredStaticAlias"]:
31+
"""Scan a template for static_alias declarations.
32+
Returns a list of DeclaredStaticAlias namedtuples.
33+
"""
34+
if _static_alias_editing_enabled is False:
35+
return []
3136
compiled_template = get_template(template)
3237
nodes = _scan_placeholders((_get_nodelist(compiled_template)), node_class=StaticAlias)
3338
placeholders = [node.get_declaration() for node in nodes]

djangocms_alias/templates/djangocms_alias/base.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{% extends "base.html" %}
2-
{% load i18n static sekizai_tags %}
2+
{% load i18n %}
33

4-
{% block title %}{% trans "Aliases" %}{% endblock %}
4+
{% block title %}{% translate "Aliases" %}{% endblock %}
55

66
{% block breadcrumb %}{% endblock %}
77
{% block footer %}{% endblock %}

djangocms_alias/templatetags/djangocms_alias_tags.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from cms.utils.placeholder import validate_placeholder_name
1010
from cms.utils.urlutils import add_url_parameters, admin_reverse
1111
from django import template
12+
from django.conf import settings
1213

1314
from ..constants import DEFAULT_STATIC_ALIAS_CATEGORY_NAME, USAGE_ALIAS_URL_NAME
1415
from ..models import Alias, AliasContent, Category
@@ -18,15 +19,17 @@
1819

1920
DeclaredStaticAlias = namedtuple("DeclaredStaticAlias", ["static_code", "site"])
2021

22+
_static_alias_editing_enabled = getattr(settings, "STATIC_ALIAS_EDITING_ENABLED", True)
23+
2124

2225
@register.simple_tag(takes_context=False)
23-
def get_alias_usage_view_url(alias, **kwargs):
26+
def get_alias_usage_view_url(alias, **kwargs) -> str:
2427
url = admin_reverse(USAGE_ALIAS_URL_NAME, args=[alias.pk])
2528
return add_url_parameters(url, **ChainMap(kwargs))
2629

2730

2831
@register.filter()
29-
def admin_view_url(obj):
32+
def admin_view_url(obj) -> str:
3033
if obj and is_editable_model(obj.__class__):
3134
# Is obj frontend-editable?
3235
return get_object_preview_url(obj)
@@ -41,12 +44,12 @@ def admin_view_url(obj):
4144

4245

4346
@register.filter()
44-
def verbose_name(obj):
47+
def verbose_name(obj) -> str:
4548
return obj._meta.verbose_name
4649

4750

4851
@register.simple_tag(takes_context=True)
49-
def render_alias(context, instance):
52+
def render_alias(context, instance) -> str:
5053
request = context["request"]
5154

5255
toolbar = get_toolbar_from_request(request)
@@ -83,7 +86,7 @@ class StaticAlias(Tag):
8386
],
8487
)
8588

86-
def _get_alias(self, request, static_code, extra_bits):
89+
def _get_alias(self, request, static_code, extra_bits) -> Alias | None:
8790
alias_filter_kwargs = {
8891
"static_code": static_code,
8992
}
@@ -116,10 +119,10 @@ def _get_alias(self, request, static_code, extra_bits):
116119
alias_creation_kwargs["site"] = current_site
117120

118121
alias = Alias.objects.create(category=default_category, **alias_creation_kwargs)
119-
120122
if (
121123
not alias.get_content(language=self.language, show_draft_content=self.get_draft_content)
122124
and request.user.is_authenticated
125+
and self.get_draft_content
123126
):
124127
alias_content = AliasContent.objects.with_user(request.user).create(
125128
alias=alias,
@@ -129,7 +132,7 @@ def _get_alias(self, request, static_code, extra_bits):
129132
alias._content_cache[self.language] = alias_content
130133
return alias
131134

132-
def render_tag(self, context, static_code, extra_bits, nodelist=None):
135+
def render_tag(self, context, static_code, extra_bits, nodelist=None) -> str:
133136
request = context.get("request")
134137

135138
if not static_code or not request:
@@ -158,16 +161,18 @@ def render_tag(self, context, static_code, extra_bits, nodelist=None):
158161
context=context,
159162
nodelist=nodelist,
160163
use_cache=True,
161-
editable=editable,
164+
editable=editable and _static_alias_editing_enabled,
162165
)
163-
if self.toolbar.edit_mode_active and not editable:
166+
if self.toolbar.edit_mode_active and not editable and _static_alias_editing_enabled:
164167
# Also non-editable placeholders need interactivity in the structure board
165168
content += renderer.get_placeholder_toolbar_js(placeholder)
166169
return content
167170
return ""
168171

169-
def get_declaration(self):
172+
def get_declaration(self) -> DeclaredStaticAlias | None:
170173
"""Used to identify static_alias declarations"""
174+
if not _static_alias_editing_enabled:
175+
return None
171176
static_code = str(self.kwargs["static_code"].var).strip('"').strip("'")
172177
site = False
173178
if isinstance(self.kwargs["extra_bits"], ListValue):

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ classifiers = [
2222
"Framework :: Django :: 5.0",
2323
"Framework :: Django :: 5.1",
2424
"Framework :: Django :: 5.2",
25+
"Framework :: Django :: 6.0",
2526
"Framework :: Django CMS :: 5.0",
2627
"Intended Audience :: Developers",
2728
"Operating System :: OS Independent",

tests/requirements/compile.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
"py311-dj52-cms50": [],
1111
"py312-dj52-cms50": [],
1212
"py313-dj52-cms50": [],
13+
"py312-dj60-cms50": [],
14+
"py313-dj60-cms50": [],
1315
"py313-djmain-cmsdev": [],
1416
"py313-djmain-cms50": [],
1517
}
@@ -20,6 +22,7 @@
2022
"dj50": "Django>=5.0,<5.1",
2123
"dj51": "Django>=5.1,<5.2",
2224
"dj52": "Django>=5.2,<5.3",
25+
"dj60": "Django>=6.0a1,<6.1",
2326
}
2427

2528
cms_dict = {

tests/requirements/py311-dj52-cms50-default.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ coverage[toml]==7.8.0
1414
# via
1515
# -r requirements.in
1616
# pytest-cov
17-
django==5.2.5
17+
django==5.2.6
1818
# via
1919
# django-classy-tags
2020
# django-cms
@@ -29,7 +29,7 @@ django-classy-tags==4.1.0
2929
# -r requirements.in
3030
# django-cms
3131
# django-sekizai
32-
django-cms==5.0.2
32+
django-cms==5.0.3
3333
# via djangocms-versioning
3434
django-formtools==2.5.1
3535
# via django-cms

0 commit comments

Comments
 (0)