10000 feat: Headless readiness by fsbraun · Pull Request #7850 · django-cms/django-cms · GitHub
[go: up one dir, main page]

Skip to content

feat: Headless readiness #7850

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 43 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
cf26a48
Feat: Move data bridge data to script tags for easier extraction.
fsbraun Mar 21, 2024
600f85a
Fix linting
fsbraun Mar 21, 2024
d0574ed
Move json script before script reading it to avoid race conditions
fsbraun Mar 21, 2024
798829e
lean template
fsbraun Mar 21, 2024
29b7e74
Merge branch 'develop-4' into feat/data-bridge
fsbraun Mar 28, 2024
3c01d6a
Merge branch 'develop-4' into feat/data-bridge
fsbraun Apr 5, 2024
50680e9
Move wizard urls to admin namespace
fsbraun May 7, 2024
73cad7b
Merge branch 'develop-4' into feat/data-bridge
fsbraun May 7, 2024
eaa4edd
Fix linting errors
fsbraun May 7, 2024
c73dc50
Update appresolver and fix page middleware (though no need to use in…
fsbraun May 7, 2024
d733f59
fix: use admin_reverse instead of reverse('admin: ...
fsbraun May 7, 2024
fd2e9be
Update preview condition for toolbar
fsbraun May 8, 2024
6c60e12
Self-contained structure mode css
fsbraun May 8, 2024
23c4d9c
Stay in 4.1.x
fsbraun May 8, 2024
e0edb6f
Merge branch 'develop-4' into feat/data-bridge
fsbraun May 9, 2024
baff66e
Alles structure endpoint for read-only objects
fsbraun May 10, 2024
2872478
fix: linting
fsbraun May 10, 2024
6c65df1
more linting
fsbraun May 10, 2024
851796f
Merge branch 'develop-4' into feat/data-bridge
May 20, 2024
700e537
feat: Allow running without templates
fsbraun May 22, 2024
0cb5fed
Merge branch 'feat/data-bridge' of github.com:fsbraun/django-cms into…
fsbraun May 22, 2024
c50e1a4
Merge branch 'develop-4' into feat/data-bridge
fsbraun May 22, 2024
d5170a4
Add unformatted preview for headless mode
fsbraun May 23, 2024
b73febb
Merge branch 'feat/data-bridge' of github.com:fsbraun/django-cms into…
fsbraun May 23, 2024
3ebffaa
Fix: Advanced placeholder config
fsbraun May 23, 2024
5dbd7ef
Fix: tests
fsbraun May 23, 2024
deff31c
Fix {% render_placeholder %} tag
fsbraun May 23, 2024
46d3da1
Merge branch 'develop-4' into feat/data-bridge
fsbraun May 25, 2024
fc6bee8
Merge branch 'develop-4' into feat/data-bridge
fsbraun May 30, 2024
f35a9d3
Merge branch 'develop-4' into feat/data-bridge
fsbraun Jun 11, 2024
1c61cb7
Add docs
fsbraun Jun 15, 2024
5721225
Merge branch 'feat/data-bridge' of github.com:fsbraun/django-cms into…
fsbraun Jun 15, 2024
cbdae33
Fix typos
fsbraun Jun 15, 2024
f6d8854
Merge branch 'develop-4' into feat/data-bridge
fsbraun Jun 18, 2024
8b5750f
fix typos
fsbraun Jun 22, 2024
5794f18
Update docs for better clarity
fsbraun Jun 25, 2024
91aad91
Merge branch 'develop-4' into feat/data-bridge
fsbraun Jun 25, 2024
ec2219e
Merge branch 'develop-4' into feat/data-bridge
fsbraun Jul 22, 2024
aaf44c0
Merge branch 'develop-4' into feat/data-bridge
fsbraun Jul 25, 2024
583ceb2
Fix: Use user language for headless plugin list
fsbraun Jul 26, 2024
d81c9c3
Fix docs typo
fsbraun Jul 26, 2024
cd025f9
Update docs after review
fsbraun Jul 29, 2024
e2af484
Merge branch 'develop-4' into feat/data-bridge
fsbraun Jul 29, 2024
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
Prev Previous commit
Next Next commit
feat: Allow running without templates
  • Loading branch information
fsbraun committed May 22, 2024
commit 700e537234d5d50aad5245a1d925d3fa1b46f204
3 changes: 3 additions & 0 deletions cms/static/cms/sass/components/_structureboard.scss
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,9 @@
box-shadow: inset 0 0 0 1px $gray-light;
}
}
.cms-drag-disabled .cms-dragitem:hover {
box-shadow: inherit;
}
.cms-dragitem-collapsable {
@include icon(arrow-wide);
&:before {
Expand Down
2 changes: 1 addition & 1 deletion cms/templates/cms/toolbar/structure.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{% load cms_tags cms_js_tags i18n sekizai_tags %}
<!DOCTYPE html>
<html class="cms-overflow cms-structure-mode-endpoint">
<html class="cms-overflow{% if not CMS_TEMPLATE %} cms-structure-mode-endpoint{% endif %}">
<head>
<meta charset="UTF-8">
<title>{{ object.title }}</title>
Expand Down
2 changes: 1 addition & 1 deletion cms/templates/cms/toolbar/toolbar_javascript.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
CMS._plugins = CMS._plugins || [];
// this is a global shared configuration
CMS.config = {
'mode': {% if cms_toolbar.edit_mode_active %}'draft'{% else %}'live'{% endif %},
'mode': {% if cms_toolbar.edit_mode_active or cms_toolbar.structure_mode_active %}'draft'{% else %}'live'{% endif %},
'auth': {% if user.is_authenticated %}true{% else %}false{% endif %},
'debug': {% if debug %}true{% else %}false{% endif %},
'csrf': '{{ csrf_token }}',
Expand Down
15 changes: 10 additions & 5 deletions 10000 cms/toolbar/toolbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,7 @@ def edit_mode_active(self):

if self.structure_mode_active:
return True

if self._resolver_match:
if self.is_staff and self._resolver_match:
return self._resolver_match.url_name == 'cms_placeholder_render_object_edit'
return False

Expand All @@ -119,9 +118,6 @@ def preview_mode_active(self):
@cached_property
def content_mode_active(self):
"""``True`` if content mode is active."""
if self.structure_mode_active:
# Structure mode always takes precedence
return False
return self.is_staff and not self.edit_mode_active

@cached_property
Expand Down Expand Up @@ -412,6 +408,15 @@ def object_is_editable(self, obj=None):
return True
return False

@property
def edit_mode_active(self):
"""``True`` if editing mode is active。"""
# Cannot be cached since it changes depending on the object.
if self.structure_mode_active:
return self.object_is_editable()
return super().edit_mode_active


# Internal API

def _add_item(self, item, position=None):
Expand Down
34 changes: 34 additions & 0 deletions cms/utils/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,40 @@ def get_class(method_name, model):
'https://django-cms.readthedocs.io/en/latest/extending_cms/extending_page_title.html#handling-relations.') # noqa


@define_check
def check_template_conf(output):
with output.section("Template configuration") as section:
if get_cms_setting("TEMPLATES"):
if isinstance(get_cms_setting("TEMPLATES"), (list, tuple)):
for template in get_cms_setting("TEMPLATES"):
if not isinstance(template, (list, tuple)):
section.error("CMS_TEMPLATES setting contains a non-list/tuple entry")
elif len(template) != 2:
section.error("CMS_TEMPLATES setting contains a list/tuple with != 2 entries")
elif not isinstance(template[0], str):
section.error("CMS_TEMPLATES contains a non-string entry")
else:
section.success("CMS_TEMPLATES_DIR or CMS_TEMPLATES setting found")
else:
section.error("CMS_TEMPLATES setting is not a list or tuple")
if hasattr(settings, "CMS_PLACEHOLDERS"):
section.warn("CMS_PLACEHOLDERS setting is also present but will be ignored.")
elif get_cms_setting("PLACEHOLDERS"):
if isinstance(get_cms_setting("PLACEHOLDERS"), (list, tuple)):
for placeholder in get_cms_setting("PLACEHOLDERS"):
if not isinstance(placeholder, (list, tuple)):
section.error("CMS_PLACEHOLDERS setting contains a non-list/tuple entry")
elif not isinstance(placeholder[0], str):
section.error("CMS_PLACEHOLDERS contains a non-string entry")
else:
section.success("CMS_PLACEHOLDERS setting entry found - CMS will run in headless mode")
else:
section.error("CMS_PLACEHOLDERS setting is not a list or tuple")
else:
section.warn("Both CMS_TEMPLATES and CMS_PLACEHOLDERS settings are missing. "
"Will run in headless mode with one placeholder called \"content\"")


def check(output):
"""
Checks the configuration/environment of this django CMS installation.
Expand Down
14 changes: 12 additions & 2 deletions cms/utils/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def wrapper():
'DEFAULT_X_FRAME_OPTIONS': constants.X_FRAME_OPTIONS_INHERIT,
'TOOLBAR_SIMPLE_STRUCTURE_MODE': True,
'PLACEHOLDER_CONF': {},
'PLACEHOLDERS': (('', ('content',)),),
'PERMISSION': False,
# Whether to use raw ID lookups for users when PERMISSION is True
'RAW_ID_USERS': False,
Expand Down Expand Up @@ -136,7 +137,7 @@ def get_templates():
if isinstance(tpldir, dict):
tpldir = tpldir[settings.SITE_ID]
# We must extract the relative path of CMS_TEMPLATES_DIR to the nearest
# valid templates directory. Here we mimic what the filesystem and
# valid templates' directory. Here we mimic what the filesystem and
# app_directories template loaders do
prefix = ''
# Relative to TEMPLATE['DIRS'] for filesystem loader
Expand Down Expand Up @@ -168,11 +169,19 @@ def get_templates():
templates = list((os.path.join(prefix, tpl), tpl) for tpl in os.listdir(tpldir))
else:
templates = list(getattr(settings, 'CMS_TEMPLATES', []))
if get_cms_setting('TEMPLATE_INHERITANCE'):
if get_cms_setting('TEMPLATE_INHERITANCE') and templates:
templates.append((constants.TEMPLATE_INHERITANCE_MAGIC, _('Inherit the template of the nearest ancestor')))
return templates


def get_placeholders():
if getattr(settings, 'CMS_PLACEHOLDERS', False):
return settings.CMS_PLACEHOLDERS
if getattr(settings, 'CMS_TEMPLATES', False) or getattr(settings, 'CMS_TEMPLATES_DIR', False):
return ()
return DEFAULTS['PLACEHOLDERS']


def _ensure_languages_settings(languages):
valid_language_keys = ['code', 'name', 'fallbacks', 'hide_untranslated', 'redirect_on_fallback', 'public']
required_language_keys = ['code', 'name']
Expand Down Expand Up @@ -272,6 +281,7 @@ def get_unihandecode_host():
'MEDIA_URL': get_media_url,
# complex because not prefixed by CMS_
'TEMPLATES': get_templates,
'PLACEHOLDERS': get_placeholders,
'LANGUAGES': get_languages,
'UNIHANDECODE_HOST': get_unihandecode_host,
'CMS_TOOLBAR_URL__PERSIST': get_toolbar_url__persist,
Expand Down
4 changes: 4 additions & 0 deletions cms/utils/page.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ def get_page_template_from_request(request):
templates = get_cms_setting('TEMPLATES')
template_names = frozenset(pair[0] for pair in templates)

if not templates:
# no templates defined, CMS is running headless
return None

if len(templates) == 1:
# there's only one template
# avoid any further computation
Expand Down
12 changes: 10 additions & 2 deletions cms/utils/placeholder.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,13 +407,21 @@ def rescan_placeholders_for_obj(obj):


def get_declared_placeholders_for_obj(obj):
"""Returns declared placeholders for an object. The object is supposed to have a method ``get_template``
"""Returns declared placeholders for an object. The object is supposed to either have a method
``get_placeholder_slots`` which returns the list of placeholders or a method ``get_template``
which returns the template path as a string that renders the object. ``get_declared_placeholders`` returns
a list of placeholders used in the template by the ``{% placeholder %}`` template tag.
"""
if hasattr(obj, "get_placeholder_slots"):
from cms.templatetags.cms_tags import DeclaredPlaceholder

return [
DeclaredPlaceholder(slot=slot, inherit=False) if isinstance(slot, str) else DeclaredPlaceholder(*slot)
for slot in obj.get_placeholder_slots()
]
if not hasattr(obj, "get_template"):
raise NotImplementedError(
"%s should implement get_template" % obj.__class__.__name__
"%s should implement either get_placeholders or get_template" % obj.__class__.__name__
)
return get_placeholders(obj.get_template())

Expand Down
0