diff --git a/core/templates/core/base.html b/core/templates/core/base.html index 2d70fb9..80e7ded 100644 --- a/core/templates/core/base.html +++ b/core/templates/core/base.html @@ -17,7 +17,7 @@ diff --git a/core/templates/core/collection_detail.html b/core/templates/core/collection_detail.html index c4be5ce..f2b7c5f 100644 --- a/core/templates/core/collection_detail.html +++ b/core/templates/core/collection_detail.html @@ -2,66 +2,37 @@ {% load i18n markdown_extras %} {% block content %}
{% trans "Collections are currently available on desktop only." %}
-
-
-
- ← {% trans "Collections" %} -

{{ collection.title }}

-
- {% if collection.team %}{{ collection.team.name }}{% else %}{{ collection.get_visibility_display }}{% endif %} - {% for tag in collection.tags.all %}#{{ tag.name }}{% endfor %} +
- - {% if collection.description %}
{{ collection.description|markdown }}
{% endif %} + {% if collection.description %}
{{ collection.description|markdown }}
{% endif %} + {% if sections %} -
{% trans "Contents" %}
    {% for section in sections %}
  1. {{ section.title|default:section.item.content|truncatechars:90 }}
  2. {% endfor %}
+ {% endif %} - {% if pending_item %} -
-

{% trans "The note has a different visibility" %}

-

{% trans "To add it, change its visibility or create a separate copy for this collection." %}

-
{{ pending_item.content|truncatechars:180 }}
-
- {% if pending_can_convert %}
{% csrf_token %}
{% endif %} -
{% csrf_token %}
- {% trans "Cancel" %} -
- {% if not pending_can_convert %}
{% trans "The original cannot be changed because it is used elsewhere or you cannot edit it." %}
{% endif %} -
- {% endif %} - -
+
{% for section in sections %} -
-
-

{{ section.title|default:section.item.content|truncatechars:100 }}

- #{{ section.item.id }} -
-
{{ section.item|item_markdown }}
- {% if section.item.comment %}
{{ section.item.comment|markdown }}
{% endif %} - {% if can_edit %}
-
{% csrf_token %}
-
{% csrf_token %}
-
{% csrf_token %}
-
{% csrf_token %}
-
{% endif %} -
+
+ {% if section.title %}

{{ section.title }}

{% endif %} + {{ section.item|item_markdown }} + {% if section.item.comment %}
{{ section.item.comment|markdown }}
{% endif %} +
{% empty %}
{% trans "This collection has no sections yet." %}
{% endfor %}
- - {% if can_edit %} -
-

{% trans "Add existing note" %}

-
-
- {% for item in candidates %}
{{ item.content|truncatechars:180 }}
#{{ item.id }} · {{ item.get_visibility_display }}{% if item.team %} · {{ item.team.name }}{% endif %}
{% csrf_token %}
{% empty %}
{% trans "No matching notes." %}
{% endfor %} -
-
- {% endif %} -
+ {% endblock %} diff --git a/core/templates/core/collection_manage.html b/core/templates/core/collection_manage.html new file mode 100644 index 0000000..fb483ae --- /dev/null +++ b/core/templates/core/collection_manage.html @@ -0,0 +1,67 @@ +{% extends 'core/base.html' %} +{% load i18n markdown_extras %} +{% block content %} +
{% trans "Collections are currently available on desktop only." %}
+
+
+
+ ← {% trans "View collection" %} +

{{ collection.title }}

+
+ {% if collection.team %}{{ collection.team.name }}{% else %}{{ collection.get_visibility_display }}{% endif %} + {% for tag in collection.tags.all %}#{{ tag.name }}{% endfor %} +
+
+ {% if can_edit %}
{% trans "Collection settings" %}{% if can_delete %}
{% csrf_token %}
{% endif %}
{% endif %} +
+ + {% if collection.description %}
{{ collection.description|markdown }}
{% endif %} + + {% if sections %} +
{% trans "Contents" %}
    {% for section in sections %}
  1. {{ section.title|default:section.item.content|truncatechars:90 }}
  2. {% endfor %}
+ {% endif %} + + {% if pending_item %} +
+

{% trans "The note has a different visibility" %}

+

{% trans "To add it, change its visibility or create a separate copy for this collection." %}

+
{{ pending_item.content|truncatechars:180 }}
+
+ {% if pending_can_convert %}
{% csrf_token %}
{% endif %} +
{% csrf_token %}
+ {% trans "Cancel" %} +
+ {% if not pending_can_convert %}
{% trans "The original cannot be changed because it is used elsewhere or you cannot edit it." %}
{% endif %} +
+ {% endif %} + +
+ {% for section in sections %} +
+
+

{{ section.title|default:section.item.content|truncatechars:100 }}

+ #{{ section.item.id }} +
+
{{ section.item|item_markdown }}
+ {% if section.item.comment %}
{{ section.item.comment|markdown }}
{% endif %} + {% if can_edit %}
+
{% csrf_token %}
+
{% csrf_token %}
+
{% csrf_token %}
+
{% csrf_token %}
+
{% endif %} +
+ {% empty %}
{% trans "This collection has no sections yet." %}
{% endfor %} +
+ + {% if can_edit %} +
+

{% trans "Add existing note" %}

+
+
+ {% for item in candidates %}
{{ item.content|truncatechars:180 }}
#{{ item.id }} · {{ item.get_visibility_display }}{% if item.team %} · {{ item.team.name }}{% endif %}
{% csrf_token %}
{% empty %}
{% trans "No matching notes." %}
{% endfor %} +
+
+ {% endif %} +
+{% endblock %} diff --git a/core/tests.py b/core/tests.py index 0277523..e8ca668 100644 --- a/core/tests.py +++ b/core/tests.py @@ -159,20 +159,22 @@ class CollectionTests(TestCase): collection = Collection.objects.get() self.assertRedirects(response, collection.get_absolute_url()) note = Item.objects.create(owner=self.user, kind=Item.Kind.NOTE, content='Introduction') - response = self.client.post(collection.get_absolute_url(), {'action': 'add', 'item_id': note.id}) - self.assertRedirects(response, collection.get_absolute_url()) + manage_url = reverse('collection_manage', args=[collection.id]) + response = self.client.post(manage_url, {'action': 'add', 'item_id': note.id}) + self.assertRedirects(response, manage_url) self.assertTrue(CollectionSection.objects.filter(collection=collection, item=note).exists()) def test_public_collection_asks_before_copying_private_note(self): collection = Collection.objects.create(owner=self.user, title='Public guide', visibility=Item.Visibility.PUBLIC) note = Item.objects.create(owner=self.user, kind=Item.Kind.NOTE, content='Private section') - response = self.client.post(collection.get_absolute_url(), {'action': 'add', 'item_id': note.id}) + manage_url = reverse('collection_manage', args=[collection.id]) + response = self.client.post(manage_url, {'action': 'add', 'item_id': note.id}) self.assertContains(response, 'name="mode" value="copy"') - response = self.client.post(collection.get_absolute_url(), { + response = self.client.post(manage_url, { 'action': 'confirm_add', 'item_id': note.id, 'mode': 'copy', }) - self.assertRedirects(response, collection.get_absolute_url()) + self.assertRedirects(response, manage_url) copied = collection.sections.get().item self.assertNotEqual(copied.id, note.id) self.assertEqual(copied.visibility, Item.Visibility.PUBLIC) @@ -199,10 +201,24 @@ class CollectionTests(TestCase): self.assertEqual(collection.team, team) self.assertEqual(collection.visibility, Item.Visibility.TEAM) + def test_collection_opens_as_continuous_reading_view(self): + collection = Collection.objects.create(owner=self.user, title='Reading view') + first = Item.objects.create(owner=self.user, kind=Item.Kind.NOTE, content='First paragraph') + second = Item.objects.create(owner=self.user, kind=Item.Kind.NOTE, content='Second paragraph') + CollectionSection.objects.create(collection=collection, item=first, title='First', position=0) + CollectionSection.objects.create(collection=collection, item=second, position=1) + response = self.client.get(collection.get_absolute_url()) + self.assertContains(response, 'collection-flow') + self.assertContains(response, reverse('collection_manage', args=[collection.id])) + self.assertNotContains(response, 'Add existing note') + self.assertNotContains(response, 'section_id') + def test_public_collection_is_visible_without_login(self): collection = Collection.objects.create(owner=self.user, title='Public', visibility=Item.Visibility.PUBLIC) self.client.logout() - self.assertEqual(self.client.get(collection.get_absolute_url()).status_code, 200) + response = self.client.get(collection.get_absolute_url()) + self.assertEqual(response.status_code, 200) + self.assertNotContains(response, reverse('collection_manage', args=[collection.id])) def test_collection_tag_is_not_removed_as_unused(self): tag = Tag.objects.create(name='documentation') diff --git a/core/urls.py b/core/urls.py index bbd82bc..0294f70 100644 --- a/core/urls.py +++ b/core/urls.py @@ -22,6 +22,7 @@ urlpatterns = [ path('collections/', views.collection_list, name='collection_list'), path('collections/new/', views.collection_create, name='collection_create'), path('collections//', views.collection_detail, name='collection_detail'), + path('collections//manage/', views.collection_manage, name='collection_manage'), path('collections//edit/', views.collection_edit, name='collection_edit'), path('collections//delete/', views.collection_delete, name='collection_delete'), path('items//', views.item_detail, name='item_detail'), diff --git a/core/views.py b/core/views.py index 8f380de..6f5e274 100644 --- a/core/views.py +++ b/core/views.py @@ -692,29 +692,40 @@ def collection_edit(request, pk): def collection_detail(request, pk): collection = get_object_or_404(visible_collections(request.user), pk=pk) + return render(request, 'core/collection_detail.html', { + 'collection': collection, + 'sections': collection.sections.select_related('item').prefetch_related('item__tags', 'item__attachments'), + 'can_edit': user_can_edit_collection(request.user, collection), + }) + + +@login_required +def collection_manage(request, pk): + collection = get_object_or_404(visible_collections(request.user), pk=pk) + if not user_can_edit_collection(request.user, collection): + return JsonResponse({'error': 'forbidden'}, status=403) + manage_url = reverse('collection_manage', args=[collection.pk]) if request.method == 'POST': - if not user_can_edit_collection(request.user, collection): - return JsonResponse({'error': 'forbidden'}, status=403) action = request.POST.get('action') if action in {'add', 'confirm_add'}: item = get_object_or_404(visible_items(request.user), pk=request.POST.get('item_id'), kind=Item.Kind.NOTE) if collection_item_is_compatible(collection, item): add_collection_section(collection, item) - return redirect(collection) + return redirect(manage_url) if action == 'add': - return render(request, 'core/collection_detail.html', collection_detail_context(request, collection, item)) + return render(request, 'core/collection_manage.html', collection_detail_context(request, collection, item)) mode = request.POST.get('mode') if mode == 'convert': if not item_can_change_for_collection(request.user, item, collection): messages.error(request, _('This note is used by an incompatible collection or cannot be edited. Create a copy instead.')) - return redirect(collection) + return redirect(manage_url) item = adapt_item_for_collection(item, collection) elif mode == 'copy': item = copy_item_for_collection(item, collection, request.user) else: - return redirect(collection) + return redirect(manage_url) add_collection_section(collection, item) - return redirect(collection) + return redirect(manage_url) section = get_object_or_404(CollectionSection, pk=request.POST.get('section_id'), collection=collection) if action == 'remove': section.delete() @@ -733,8 +744,8 @@ def collection_detail(request, pk): CollectionSection.objects.filter(pk=section.pk).update(position=temporary) CollectionSection.objects.filter(pk=other.pk).update(position=section.position) CollectionSection.objects.filter(pk=section.pk).update(position=other.position) - return redirect(collection) - return render(request, 'core/collection_detail.html', collection_detail_context(request, collection)) + return redirect(manage_url) + return render(request, 'core/collection_manage.html', collection_detail_context(request, collection)) @login_required diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 62766f9..e0451e1 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -664,6 +664,14 @@ msgstr "Sammlungen sind derzeit nur auf dem Desktop verfügbar." msgid "Edit collection" msgstr "Sammlung bearbeiten" +#: core/templates/core/collection_manage.html +msgid "View collection" +msgstr "Sammlung ansehen" + +#: core/templates/core/collection_manage.html +msgid "Collection settings" +msgstr "Sammlungseinstellungen" + #: core/templates/core/collection_detail.html msgid "Delete collection? The notes remain available." msgstr "Sammlung löschen? Die Notizen bleiben erhalten."