Skip to content

Commit e0c5e64

Browse files
authored
feat: Preserve GET params for "View published" button (#509)
* feat: Preserve GET params for "View published" button * fix: Update helper * fix: Preserve GET params on edit_redirect * fix: Only add search string if GET params present * fix: Helper signature * Add tests * add final test
1 parent 85e5c46 commit e0c5e64

File tree

6 files changed

+137
-5
lines changed

6 files changed

+137
-5
lines changed

djangocms_versioning/admin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1222,7 +1222,7 @@ def edit_redirect_view(self, request, object_id):
12221222
return redirect(version_list_url(version.content))
12231223

12241224
# Redirect
1225-
return redirect(get_editable_url(target.content, request.GET.get("force_admin")))
1225+
return redirect(get_editable_url(target.content, request.GET.get("force_admin"), request.GET))
12261226

12271227
def revert_view(self, request, object_id):
12281228
"""Reverts to the specified version i.e. creates a draft from it."""

djangocms_versioning/cms_toolbars.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ def _add_edit_button(self, disabled=False):
107107
f"admin:{proxy_model._meta.app_label}_{proxy_model.__name__.lower()}_edit_redirect",
108108
args=(version.pk,),
109109
)
110+
if self.request.GET:
111+
edit_url += "?" + self.request.GET.urlencode()
110112
pks_for_grouper = version.versionable.for_content_grouping_values(version.content).values_list(
111113
"pk", flat=True
112114
)
@@ -254,7 +256,8 @@ def _add_view_published_button(self):
254256

255257
url = None
256258
if hasattr(published_version, "get_absolute_url"):
257-
url = get_object_live_url(published_version, site=get_current_site(self.toolbar.request))
259+
request = self.toolbar.request
260+
url = get_object_live_url(published_version, site=get_current_site(request), params=request.GET)
258261
if url and (self.toolbar.edit_mode_active or self.toolbar.preview_mode_active):
259262
item = ButtonList(side=self.toolbar.RIGHT)
260263
item.add_button(

djangocms_versioning/helpers.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
from cms.utils import get_current_site # noqa F401
3636
except ImportError:
3737
# cms < 5.1
38-
def get_object_live_url(obj, language=None, site=None) -> str:
38+
def get_object_live_url(obj, language=None, site=None, params=None) -> str:
3939
with force_language(language):
4040
return obj.get_absolute_url()
4141

@@ -265,13 +265,15 @@ def is_content_editable(placeholder: Placeholder, user: models.Model) -> bool:
265265
return version.state == DRAFT
266266

267267

268-
def get_editable_url(content_obj, force_admin=False):
268+
def get_editable_url(content_obj, force_admin=False, params=None):
269269
"""If the object is editable the cms editable view should be used, with the toolbar.
270270
This method provides the URL for it.
271271
"""
272272
if is_editable_model(content_obj.__class__) and not force_admin:
273273
language = getattr(content_obj, "language", None)
274274
url = get_object_edit_url(content_obj, language)
275+
if params:
276+
url += "?" + params.urlencode()
275277
# Or else, the standard edit view should be used
276278
else:
277279
url = admin_reverse(

tests/test_admin.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2074,6 +2074,40 @@ def test_edit_redirect_view_non_editable_object_endpoint(self):
20742074

20752075
self.assertRedirects(response, target_url, target_status_code=302)
20762076

2077+
def test_edit_redirect_view_preserves_get_params_for_editable_target(self):
2078+
page_versionable = VersioningCMSConfig.versioning[0]
2079+
page_version = factories.PageVersionFactory(content__language="en")
2080+
url = self.get_admin_url(
2081+
page_versionable.version_model_proxy, "edit_redirect", page_version.pk
2082+
)
2083+
params = {"foo": "bar", "next": "/return/"}
2084+
2085+
with self.login_user_context(self.superuser):
2086+
response = self.client.post(f"{url}?{urlencode(params)}")
2087+
2088+
parsed = urlparse(response.url)
2089+
self.assertEqual(
2090+
parsed.path, get_object_edit_url(page_version.content, language="en")
2091+
)
2092+
self.assertEqual(parse_qs(parsed.query), {"foo": ["bar"], "next": ["/return/"]})
2093+
2094+
def test_edit_redirect_view_drops_get_params_for_admin_redirect(self):
2095+
draft_version = factories.PollVersionFactory(state=constants.DRAFT)
2096+
url = self.get_admin_url(
2097+
self.versionable.version_model_proxy, "edit_redirect", draft_version.pk
2098+
)
2099+
params = {"force_admin": "1", "next": "/return/"}
2100+
2101+
with self.login_user_context(self.superuser):
2102+
response = self.client.post(f"{url}?{urlencode(params)}")
2103+
2104+
parsed = urlparse(response.url)
2105+
self.assertEqual(
2106+
parsed.path,
2107+
self.get_admin_url(PollContent, "change", draft_version.content.pk),
2108+
)
2109+
self.assertEqual(parse_qs(parsed.query), {})
2110+
20772111
@patch("django.contrib.messages.add_message")
20782112
def test_edit_redirect_view_handles_nonexistent_version(self, mocked_messages):
20792113
url = self.get_admin_url(

tests/test_helpers.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from unittest import skipIf
2+
from urllib.parse import parse_qs, urlparse
3+
4+
from cms import __version__ as cms_version
5+
from cms.test_utils.testcases import CMSTestCase
6+
from cms.toolbar.utils import get_object_edit_url
7+
from django.http import QueryDict
8+
from packaging.version import Version as PackageVersion
9+
10+
from djangocms_versioning import helpers
11+
from djangocms_versioning.test_utils import factories
12+
from djangocms_versioning.test_utils.polls.models import PollContent
13+
14+
15+
class HelperUrlFunctionsTestCase(CMSTestCase):
16+
def test_get_editable_url_appends_params_for_editable_content(self):
17+
version = factories.PageVersionFactory(content__language="en")
18+
params = QueryDict("foo=bar&baz=qux")
19+
20+
url = helpers.get_editable_url(version.content, params=params)
21+
22+
parsed = urlparse(url)
23+
self.assertEqual(
24+
parsed.path, get_object_edit_url(version.content, language="en")
25+
)
26+
self.assertEqual(parse_qs(parsed.query), {"foo": ["bar"], "baz": ["qux"]})
27+
28+
def test_get_editable_url_does_not_append_params_for_admin_change(self):
29+
poll_version = factories.PollVersionFactory()
30+
params = QueryDict("foo=bar&force_admin=1")
31+
32+
url = helpers.get_editable_url(
33+
poll_version.content, force_admin=True, params=params
34+
)
35+
36+
parsed = urlparse(url)
37+
self.assertEqual(
38+
parsed.path,
39+
helpers.get_admin_url(PollContent, "change", poll_version.content.pk),
40+
)
41+
self.assertEqual(parse_qs(parsed.query), {})
42+
43+
@skipIf(
44+
PackageVersion(cms_version) < PackageVersion("5.1"),
45+
"get_object_live_url params supported only on django CMS 5.1+",
46+
)
47+
def test_get_object_live_url_appends_params(self):
48+
poll_version = factories.PollVersionFactory(content__language="en")
49+
params = QueryDict("foo=bar&baz=2")
50+
51+
url = helpers.get_object_live_url(
52+
poll_version.content, language="en", params=params
53+
)
54+
55+
parsed = urlparse(url)
56+
self.assertEqual(parsed.path, poll_version.content.get_absolute_url())
57+
self.assertEqual(parse_qs(parsed.query), {"foo": ["bar"], "baz": ["2"]})

tests/test_toolbars.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
from djangocms_versioning import cms_toolbars
1616
from djangocms_versioning.cms_config import VersioningCMSConfig
17-
from djangocms_versioning.cms_toolbars import VersioningPageToolbar
17+
from djangocms_versioning.cms_toolbars import VersioningPageToolbar, VersioningToolbar
1818
from djangocms_versioning.constants import ARCHIVED, DRAFT, PUBLISHED
1919
from djangocms_versioning.helpers import version_list_url
2020
from djangocms_versioning.test_utils.factories import (
@@ -255,6 +255,42 @@ def test_default_edit_button_from_cms_exists(self):
255255
self.assertEqual(len(found_button_list), 1)
256256
self.assertNotEqual(found_button_list[0].url, edit_url)
257257

258+
def test_edit_button_preserves_get_params(self):
259+
"""
260+
The edit button URL should contain all GET parameters from the request
261+
"""
262+
from cms.toolbar.toolbar import CMSToolbar
263+
from django.test import RequestFactory
264+
265+
version = PageVersionFactory(content__template="")
266+
# Create a request with GET parameters
267+
request = RequestFactory().get("/?foo=bar&baz=qux")
268+
request.user = self.get_superuser()
269+
request.session = {}
270+
request.current_page = version.content.page
271+
request.toolbar = CMSToolbar(request)
272+
273+
toolbar = VersioningToolbar(
274+
request, toolbar=request.toolbar, is_current_app=True, app_path="/"
275+
)
276+
toolbar.toolbar.set_object(version.content)
277+
toolbar.toolbar.edit_mode_active = False
278+
toolbar.toolbar.preview_mode_active = True
279+
toolbar.toolbar.structure_mode_active = False
280+
toolbar.populate()
281+
toolbar.post_template_populate()
282+
283+
edit_buttons = find_toolbar_buttons("Edit", toolbar.toolbar)
284+
self.assertTrue(len(edit_buttons) > 0, "Edit button should be present in toolbar")
285+
edit_button = edit_buttons[0]
286+
287+
# The edit button URL should include the GET parameters
288+
self.assertIn("foo=bar", edit_button.url)
289+
self.assertIn("baz=qux", edit_button.url)
290+
# The base URL should still be the edit redirect URL
291+
base_url = self._get_edit_url(version, VersioningCMSConfig.versioning[0])
292+
self.assertIn(base_url, edit_button.url)
293+
258294
def test_version_menu_for_non_version_content(self):
259295
# User objects are not registered with versioning, so attempting
260296
# to populate toolbar shouldn't contain a version menu

0 commit comments

Comments
 (0)