From b4e624cdd2a62f231c562287474c54a1c582a1dc Mon Sep 17 00:00:00 2001 From: Thomas Koopman Date: Wed, 18 Dec 2024 19:49:54 +0100 Subject: [PATCH] Use fullpage markdown widget for doc pages --- app/grandchallenge/documentation/forms.py | 19 +++++-- .../documentation/docpage_content_update.html | 11 ++++ .../documentation/docpage_detail.html | 11 ++-- .../templates/documentation/docpage_list.html | 3 +- app/grandchallenge/documentation/urls.py | 15 +++++- app/grandchallenge/documentation/views.py | 31 +++++++++-- app/tests/documentation_tests/test_views.py | 54 +++++++++++++++---- 7 files changed, 119 insertions(+), 25 deletions(-) create mode 100644 app/grandchallenge/documentation/templates/documentation/docpage_content_update.html diff --git a/app/grandchallenge/documentation/forms.py b/app/grandchallenge/documentation/forms.py index bea49862ff..7575fe37e1 100644 --- a/app/grandchallenge/documentation/forms.py +++ b/app/grandchallenge/documentation/forms.py @@ -2,7 +2,8 @@ from crispy_forms.layout import Submit from django import forms -from grandchallenge.core.widgets import MarkdownEditorInlineWidget +from grandchallenge.core.forms import SaveFormInitMixin +from grandchallenge.core.widgets import MarkdownEditorFullPageWidget from grandchallenge.documentation.models import DocPage @@ -14,13 +15,21 @@ def __init__(self, *args, **kwargs): class Meta: model = DocPage - fields = ("title", "content", "parent") - widgets = {"content": MarkdownEditorInlineWidget} + fields = ("title", "parent") -class DocPageUpdateForm(DocPageCreateForm): - """Like the create form but you can also move the page.""" +class DocPageMetadataUpdateForm(DocPageCreateForm): + """Like the create form, but you can also move the page.""" position = forms.IntegerField() position.label = "Move to index position" position.required = False + + +class DocPageContentUpdateForm(SaveFormInitMixin, forms.ModelForm): + class Meta: + model = DocPage + fields = ("content",) + widgets = { + "content": MarkdownEditorFullPageWidget, + } diff --git a/app/grandchallenge/documentation/templates/documentation/docpage_content_update.html b/app/grandchallenge/documentation/templates/documentation/docpage_content_update.html new file mode 100644 index 0000000000..07b977a99e --- /dev/null +++ b/app/grandchallenge/documentation/templates/documentation/docpage_content_update.html @@ -0,0 +1,11 @@ +{% extends "documentation/docpage_form.html" %} +{% load crispy from crispy_forms_tags %} + +{% block container %}container-fluid{% endblock %} + +{% block outer_content %} + +

Update Page

+ + {% crispy form %} +{% endblock %} diff --git a/app/grandchallenge/documentation/templates/documentation/docpage_detail.html b/app/grandchallenge/documentation/templates/documentation/docpage_detail.html index e2d6729ffa..0a295a551a 100644 --- a/app/grandchallenge/documentation/templates/documentation/docpage_detail.html +++ b/app/grandchallenge/documentation/templates/documentation/docpage_detail.html @@ -122,9 +122,14 @@
{% if 'documentation.change_docpage' in perms %} - Page overview - Add - Edit + Page overview + Add + + + + + + {% endif %}
diff --git a/app/grandchallenge/documentation/templates/documentation/docpage_list.html b/app/grandchallenge/documentation/templates/documentation/docpage_list.html index 2a723f13b5..652c744278 100644 --- a/app/grandchallenge/documentation/templates/documentation/docpage_list.html +++ b/app/grandchallenge/documentation/templates/documentation/docpage_list.html @@ -50,7 +50,8 @@

Documentation Pages

{% if 'documentation.change_docpage' in perms %} - + + {% endif %} diff --git a/app/grandchallenge/documentation/urls.py b/app/grandchallenge/documentation/urls.py index 2e21d09bc7..6bbab9e631 100644 --- a/app/grandchallenge/documentation/urls.py +++ b/app/grandchallenge/documentation/urls.py @@ -1,19 +1,30 @@ from django.urls import path from grandchallenge.documentation.views import ( + DocPageContentUpdate, DocPageCreate, DocPageDetail, DocPageList, - DocPageUpdate, + DocPageMetadataUpdate, DocumentationHome, ) app_name = "documentation" + urlpatterns = [ path("", DocumentationHome.as_view(), name="home"), path("overview/", DocPageList.as_view(), name="list"), path("create/", DocPageCreate.as_view(), name="create"), path("/", DocPageDetail.as_view(), name="detail"), - path("/update/", DocPageUpdate.as_view(), name="update"), + path( + "/content-update/", + DocPageContentUpdate.as_view(), + name="content-update", + ), + path( + "/metadata-update/", + DocPageMetadataUpdate.as_view(), + name="metadata-update", + ), ] diff --git a/app/grandchallenge/documentation/views.py b/app/grandchallenge/documentation/views.py index d7297b607a..c8c13e7c43 100644 --- a/app/grandchallenge/documentation/views.py +++ b/app/grandchallenge/documentation/views.py @@ -11,11 +11,12 @@ from guardian.mixins import LoginRequiredMixin from grandchallenge.documentation.forms import ( + DocPageContentUpdateForm, DocPageCreateForm, - DocPageUpdateForm, + DocPageMetadataUpdateForm, ) from grandchallenge.documentation.models import DocPage -from grandchallenge.subdomains.utils import reverse_lazy +from grandchallenge.subdomains.utils import reverse, reverse_lazy class DocPageList(ListView): @@ -72,9 +73,11 @@ def get_object(self, queryset=None): return get_object_or_404(DocPage, order=1) -class DocPageUpdate(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): +class DocPageMetadataUpdate( + LoginRequiredMixin, PermissionRequiredMixin, UpdateView +): model = DocPage - form_class = DocPageUpdateForm + form_class = DocPageMetadataUpdateForm permission_required = "documentation.change_docpage" raise_exception = True login_url = reverse_lazy("account_login") @@ -85,9 +88,29 @@ def form_valid(self, form): return response +class DocPageContentUpdate( + LoginRequiredMixin, PermissionRequiredMixin, UpdateView +): + model = DocPage + form_class = DocPageContentUpdateForm + template_name_suffix = "_content_update" + permission_required = "documentation.change_docpage" + raise_exception = True + login_url = reverse_lazy("account_login") + + class DocPageCreate(LoginRequiredMixin, PermissionRequiredMixin, CreateView): model = DocPage form_class = DocPageCreateForm permission_required = "documentation.add_docpage" raise_exception = True login_url = reverse_lazy("account_login") + + def get_success_url(self): + """On successful creation, go to content update.""" + return reverse( + "documentation:content-update", + kwargs={ + "slug": self.object.slug, + }, + ) diff --git a/app/tests/documentation_tests/test_views.py b/app/tests/documentation_tests/test_views.py index aa03ff7cd2..df8211034b 100644 --- a/app/tests/documentation_tests/test_views.py +++ b/app/tests/documentation_tests/test_views.py @@ -13,14 +13,18 @@ "view, perm", [ ("documentation:create", "documentation.add_docpage"), - ("documentation:update", "documentation.change_docpage"), + ("documentation:content-update", "documentation.change_docpage"), + ("documentation:metadata-update", "documentation.change_docpage"), ], ) def test_permissions(client, view, perm): u1 = UserFactory() p1 = DocPageFactory() - if view == "documentation:update": + if view in ( + "documentation:content-update", + "documentation:metadata-update", + ): reverse_kwargs = {"slug": p1.slug} else: reverse_kwargs = None @@ -43,6 +47,7 @@ def test_permissions(client, view, perm): def test_docpage_create(client): u1 = UserFactory() assign_perm("documentation.add_docpage", u1) + assign_perm("documentation.change_docpage", u1) content = "

Example content

" title = "Test title" @@ -51,12 +56,21 @@ def test_docpage_create(client): viewname="documentation:create", client=client, method=client.post, - data={"title": title, "content": content}, + data={"title": title}, user=u1, ) assert response.status_code == 302 assert DocPage.objects.count() == 1 + assert response.url.endswith("test-title/content-update/") + response = get_view_for_user( + url=response.url, + client=client, + method=client.post, + data={"content": content}, + user=u1, + ) + assert response.status_code == 302 response = get_view_for_user(url=response.url, client=client) @@ -65,7 +79,30 @@ def test_docpage_create(client): @pytest.mark.django_db -def test_docpage_update(client): +def test_docpage_content_update(client): + u1 = UserFactory() + p = DocPageFactory() + assign_perm("documentation.change_docpage", u1) + + new_content = "

New content

" + + # change content of p + response = get_view_for_user( + viewname="documentation:content-update", + client=client, + method=client.post, + reverse_kwargs={"slug": p.slug}, + data={"content": new_content}, + user=u1, + ) + + assert response.status_code == 302 + p.refresh_from_db() + assert p.content == new_content + + +@pytest.mark.django_db +def test_docpage_position_update(client): u1 = UserFactory() _ = DocPageFactory() p2 = DocPageFactory() @@ -73,22 +110,19 @@ def test_docpage_update(client): assert p2.order == 2 - new_content = "

New content

" - - # change content and order of p2 + # change order of p2 response = get_view_for_user( - viewname="documentation:update", + viewname="documentation:metadata-update", client=client, method=client.post, reverse_kwargs={"slug": p2.slug}, - data={"title": p2.title, "content": new_content, "position": 1}, + data={"title": p2.title, "position": 1}, user=u1, ) assert response.status_code == 302 p2.refresh_from_db() assert p2.order == 1 - assert p2.content == new_content @pytest.mark.django_db