Skip to content

Commit

Permalink
feat: Add versioning actions to settings (admin change view) of versi…
Browse files Browse the repository at this point in the history
…oned objects (#408)

* fix: Add versioning actions to change forms

* Fix ruff errors

* Fix settings button

* Add tests

* Only offer publish button publishing technically is possible

* fix: Unify edit icons

* Fix: Only offer settings button if the admin change view exists.

* Fix ruff issue

* Improve DRY in tests
  • Loading branch information
fsbraun authored May 23, 2024
1 parent adce880 commit 34577f0
Show file tree
Hide file tree
Showing 11 changed files with 336 additions and 26 deletions.
61 changes: 53 additions & 8 deletions djangocms_versioning/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from cms.models import PageContent
from cms.utils import get_language_from_request
from cms.utils.conf import get_cms_setting
from cms.utils.helpers import is_editable_model
from cms.utils.urlutils import add_url_parameters, static_with_version
from django.conf import settings
from django.contrib import admin, messages
Expand Down Expand Up @@ -464,10 +465,26 @@ def _get_edit_link(self, obj, request, disabled=False):
f"admin:{version._meta.app_label}_{version._meta.model_name}_edit_redirect",
args=(version.pk,),
)
# Only show if no draft exists
if version.state == PUBLISHED:
pks_for_grouper = version.versionable.for_content_grouping_values(
obj
).values_list("pk", flat=True)
drafts = Version.objects.filter(
object_id__in=pks_for_grouper,
content_type=version.content_type,
state=DRAFT,
)
if drafts.exists():
return ""
icon = "edit-new"
else:
icon = "edit"

return self.admin_action_button(
url,
icon="pencil",
title=_("Edit"),
icon=icon,
title=_("Edit") if icon == "edit" else _("New Draft"),
name="edit",
disabled=disabled,
action="post",
Expand Down Expand Up @@ -747,7 +764,7 @@ def _get_edit_link(self, obj, request, disabled=False):
return ""
icon = "edit-new"
else:
icon = "pencil"
icon = "edit"

# Don't open in the sideframe if the item is not sideframe compatible
keepsideframe = obj.versionable.content_model_is_sideframe_editable
Expand All @@ -759,7 +776,7 @@ def _get_edit_link(self, obj, request, disabled=False):
return self.admin_action_button(
edit_url,
icon=icon,
title=_("Edit") if icon == "pencil" else _("New Draft"),
title=_("Edit") if icon == "edit" else _("New Draft"),
name="edit",
action="post",
disabled=disabled,
Expand Down Expand Up @@ -822,6 +839,32 @@ def _get_unlock_link(self, obj, request):
disabled=not obj.check_unlock.as_bool(request.user),
)

def _get_settings_link(self, obj, request):
"""
Generate a settings button for the Versioning Admin
"""

# If the content object is not registered for frontend editing no action should be present
# Also, the content object must be registered with the admin site
content_model = obj.versionable.content_model
if not is_editable_model(content_model):
return ""

try:
settings_url = reverse(
f"admin:{content_model._meta.app_label}_{content_model._meta.model_name}_change",
args=(obj.content.pk,)
)
except Resolver404:
return ""

return self.admin_action_button(
settings_url,
icon="settings",
title=_("Settings"),
name="settings",
)

def get_actions_list(self):
"""Returns all action links as a list"""
return self.get_state_actions()
Expand All @@ -848,6 +891,7 @@ def get_state_actions(self):
self._get_revert_link,
self._get_discard_link,
self._get_unlock_link,
self._get_settings_link,
]

@admin.action(
Expand Down Expand Up @@ -945,19 +989,20 @@ def publish_view(self, request, object_id):
request, self.model._meta, object_id
)

requested_redirect = request.GET.get("next", None)
if conf.ON_PUBLISH_REDIRECT in ("preview", "published"):
redirect_url=get_preview_url(version.content)
else:
redirect_url=version_list_url(version.content)

if not version.can_be_published():
self.message_user(request, _("Version cannot be published"), messages.ERROR)
return redirect(redirect_url)
return redirect(requested_redirect or redirect_url)
try:
version.check_publish(request.user)
except ConditionFailed as e:
self.message_user(request, force_str(e), messages.ERROR)
return redirect(redirect_url)
return redirect(requested_redirect or redirect_url)

# Publish the version
version.publish(request.user)
Expand All @@ -970,7 +1015,7 @@ def publish_view(self, request, object_id):
if hasattr(version.content, "get_absolute_url"):
redirect_url = version.content.get_absolute_url() or redirect_url

return redirect(redirect_url)
return redirect(requested_redirect or redirect_url)

def unpublish_view(self, request, object_id):
"""Unpublishes the specified version and redirects back to the
Expand Down Expand Up @@ -1085,7 +1130,7 @@ def edit_redirect_view(self, request, object_id):
return redirect(version_list_url(version.content))

# Redirect
return redirect(get_editable_url(target.content))
return redirect(get_editable_url(target.content, request.GET.get("force_admin")))

def revert_view(self, request, object_id):
"""Reverts to the specified version i.e. creates a draft from it."""
Expand Down
11 changes: 2 additions & 9 deletions djangocms_versioning/cms_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ def on_page_content_archive(version):


class VersioningCMSPageAdminMixin(VersioningAdminMixin):
change_form_template = "admin/djangocms_versioning/page/change_form.html"

def get_readonly_fields(self, request, obj=None):
fields = super().get_readonly_fields(request, obj)
if obj:
Expand All @@ -281,15 +283,6 @@ def get_readonly_fields(self, request, obj=None):
fields.remove(f_name)
return fields

def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
if obj:
version = Version.objects.get_for_content(obj)
if not version.check_modify.as_bool(request.user):
for f_name in ["slug", "overwrite_url"]:
form.declared_fields[f_name].widget.attrs["readonly"] = True
return form

def get_queryset(self, request):
urls = ("cms_pagecontent_get_tree",)
queryset = super().get_queryset(request)
Expand Down
3 changes: 3 additions & 0 deletions djangocms_versioning/cms_toolbars.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,9 @@ def get_page_content(self, language=None):
if not language:
language = self.current_lang

toolbar_obj = self.toolbar.get_object()
if toolbar_obj and toolbar_obj.language == language:
return self.toolbar.get_object()
return get_latest_admin_viewable_content(self.page, language=language)

def populate(self):
Expand Down
6 changes: 3 additions & 3 deletions djangocms_versioning/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,11 +233,11 @@ def is_content_editable(placeholder, user):
return version.state == DRAFT


def get_editable_url(content_obj):
def get_editable_url(content_obj, force_admin=False):
"""If the object is editable the cms editable view should be used, with the toolbar.
This method is provides the URL for it.
This method provides the URL for it.
"""
if is_editable_model(content_obj.__class__):
if is_editable_model(content_obj.__class__) and not force_admin:
language = getattr(content_obj, "language", None)
url = get_object_edit_url(content_obj, language)
# Or else, the standard edit view should be used
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.object-tools a.accent {
background-color: var(--accent) !important;
}
.object-tools a.accent:hover,
.object-tools a.accent:active,
.object-tools a.accent:hover:active {
background-color: color-mix(in srgb, var(--accent) 90%, var(--dca-black)) !important;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
(function($) {
$(document).ready(function() {
$('.cms-form-post-method').on('click', function(e) {
e.preventDefault();
var csrf_token = document.querySelector('form input[name="csrfmiddlewaretoken"]').value;
var url = this.href;
var $form = $('<form method="post" action="' + url + '"></form>');
var $csrf = $(`<input type="hidden" name="csrfmiddlewaretoken" value="${csrf_token}">`);
$form.append($csrf);
$form.appendTo('body').submit();
});
});
})(django.jQuery);
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
{% extends "admin/cms/page/change_form.html" %}
{% load static admin_urls admin_modify djangocms_versioning i18n cms_admin %}

{% block extrahead %}
{{ block.super }}
<script src="{% static "djangocms_versioning/js/object-tools.js" %}"></script>
{% endblock %}
{% block extrastyle %}
{{ block.super }}
<link rel="stylesheet" href="{% static "djangocms_versioning/css/object-tools.css" %}">
{% endblock %}

{% block content_title %}
{% if title %}<h1>{{ title }}{% if original %} - {{ original.versions.first.short_name }}{% endif %}</h1>{% endif %}
{% block object-tools %}
{% if not popup and not add %}
<ul class="object-tools hide-in-modal">
{% block object-tools-items %}
{% include "admin/djangocms_versioning/versioning_buttons.html" %}
<li>
{% get_preview_url original as admin_url %}
<a href="{{ admin_url }}" target="_parent">{% trans "Preview" %}</a>
</li>
{% endblock %}
</ul>
{% endif %}
{% endblock %}
{% endblock %}

{% block content %}
<div id="content-main">



<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="?language={{ language }}{%if request.GET.parent_node %}&amp;parent_node={{ request.GET.parent_node }}{% endif %}{%if request.GET.source %}&amp;source={{ request.GET.source }}{% endif %}" method="post" id="{{ opts.model_name }}_form">
{% csrf_token %}
{% block form_top %}{% endblock %}

{% if show_language_tabs and not show_permissions %}
<div id="page_form_lang_tabs">
{% for lang_code, lang_name in language_tabs %}
<input type="button" onclick="CMS.API.changeLanguage('{% get_admin_url_for_language cms_page lang_code %}');"
class="language_button {% if lang_code == language %}selected{% else %}{% if not lang_code in filled_languages %} notfilled{% endif %}{% endif %}"
id="{{lang_code}}button" name="{{lang_code}}" value="{{lang_name}}" />
{% endfor %}
<div class="lang_tabs_line"></div>
</div>
{% endif %}

<div id="lang_tab_content">
{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
{% if save_on_top %}{% submit_row %}{% endif %}
{% if errors %}
<p class="errornote">
{% blocktrans count errors|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %}
</p>
<ul class="errorlist">{% for error in adminform.form.non_field_errors %}<li>{{ error }}</li>{% endfor %}</ul>
{% endif %}

{% for fieldset in adminform %}
{% include "admin/cms/page/includes/fieldset.html" %}
{% endfor %}

{% for inline_admin_formset in inline_admin_formsets %}
{% include inline_admin_formset.opts.template %}
{% endfor %}

{% if show_permissions %}
<div class="inline-group">
<div class="tabular inline-related">
<fieldset id="inherited_permissions" class="module aligned collapse">
<h2>{% trans 'All permissions' %}</h2>
<div class="loading" rel="../permissions/">{% trans 'Loading...' %}</div>
</fieldset>
</div>
</div>
{% endif %}

{% block after_related_objects %}{% endblock %}

{% if add %}
<div class="submit-row"{% if is_popup %} style="overflow: auto;"{% endif %}>
<input type="submit" name="_save" class="default" value="{% trans 'Save' %}" {{ onclick_attrib }}/>
<input type="submit" name="_continue" value="{% trans 'Save and continue editing' %}" {{ onclick_attrib }}/>
</div>
{% else %}
{% page_submit_row %}
{% endif %}
</div>
</form>
</div>

{% block admin_change_form_document_ready %}
{{ block.super }}
{% endblock %}

{# JavaScript for prepopulated fields #}
{% prepopulated_fields_js %}

{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{% load djangocms_versioning i18n %}
{% with url=original|url_publish_version:request.user %}
{% if url %}
<li>
<a class="accent cms-form-post-method" href="{{ url }}?next={{ request.path }}">{% trans "Publish" %}</a>
</li>
{% endif %}
{% endwith %}
{% with url=original|url_new_draft:request.user %}
{% if url %}
<li>
<a class="accent cms-form-post-method" href="{{ url }}?force_admin=1">{% trans "New Draft" %}</a>
</li>
{% endif %}
{% endwith %}
{% with url=original|url_revert_version:request.user %}
{% if url %}
<li>
<a href="{{ url }}?next={{ request.path }}">{% trans "Revert" %}</a>
</li>
{% endif %}
{% endwith %}

{% with url=original|url_version_list %}
{% if url %}
<li>
<a href="{{ url }}">{% trans "Versions" %}</a>
</li>
{% endif %}
{% endwith %}
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
{% extends versioning_fallback_change_form_template|default:"admin/change_form.html" %}
{% load i18n admin_urls djangocms_versioning %}
{% load static %}

{% block extrahead %}
{{ block.super }}
<script src="{% static "djangocms_versioning/js/object-tools.js" %}"></script>
{% endblock %}

{% block extrastyle %}
{{ block.super }}
<link rel="stylesheet" href="{% static "djangocms_versioning/css/object-tools.css" %}">
{% endblock %}

{% block object-tools-items %}
<li>
<a href="{{ original|url_version_list }}">
{% translate "Versions" %}
</a>
</li>
{% include "admin/djangocms_versioning/versioning_buttons.html" %}
{{ block.super }}
{% endblock %}

Loading

0 comments on commit 34577f0

Please sign in to comment.