Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 42 additions & 0 deletions pdfding/e2e/test_collection_e2e.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from unittest.mock import patch

from django.contrib.auth.models import User
from django.urls import reverse
from helpers import PdfDingE2ETestCase
Expand Down Expand Up @@ -33,3 +35,43 @@ def test_change_collection(self):
self.page.locator("#current_collection_name").click()
self.page.locator("#collection_modal").get_by_text("other_collection").click()
expect(self.page.locator("#current_collection_name")).to_contain_text("other_collection")

def test_details(self):
collection = self.user.profile.current_collection

with sync_playwright() as p:
self.open(reverse('collection_details', kwargs={'identifier': collection.id}), p)

expect(self.page.locator("#name")).to_contain_text("Defaul")
expect(self.page.locator("#default_collection")).to_contain_text("Yes")
expect(self.page.locator("#description")).to_contain_text("Default Collection")

@patch('pdf.services.collection_services.move')
def test_change_details(self, mock_move):
collection = self.user.profile.current_collection

with sync_playwright() as p:
self.open(reverse('collection_details', kwargs={'identifier': collection.id}), p)

self.page.locator("#name-edit").click()
self.page.locator("#id_name").dblclick()
self.page.locator("#id_name").fill("other-name")
self.page.get_by_role("button", name="Submit").click()
expect(self.page.locator("#name")).to_contain_text("other-name")
self.page.locator("#description-edit").click()
self.page.locator("#id_description").click()
self.page.locator("#id_description").fill("other description")
self.page.get_by_role("button", name="Submit").click()
expect(self.page.locator("#description")).to_contain_text("other description")

def test_details_change_collection(self):
create_collection(self.user.profile.current_workspace, 'other_collection')

collection = self.user.profile.current_collection

with sync_playwright() as p:
self.open(reverse('collection_details', kwargs={'identifier': collection.id}), p)
expect(self.page.locator("#current_collection_name")).to_contain_text("Default")
self.page.locator("#current_collection_name").click()
self.page.get_by_text("other_collection").click()
expect(self.page.locator("#current_collection_name")).to_contain_text("other_collection")
24 changes: 15 additions & 9 deletions pdfding/e2e/test_workspace_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,17 @@ def test_change_workspace(self):
expect(self.page.locator("#current_collection_name")).to_contain_text("All")

def test_details(self):
ws = self.user.profile.current_workspace

with sync_playwright() as p:
self.open(reverse('workspace_details', kwargs={'identifier': ws.id}), p)
self.open(reverse('workspace_details'), p)

expect(self.page.locator("#name")).to_contain_text("Personal")
expect(self.page.locator("#personal_workspace")).to_contain_text("Yes")
expect(self.page.locator("#description")).to_contain_text("Personal Workspace")

def test_change_details(self):
ws = self.user.profile.current_workspace

# also test changing from inactive to active
with sync_playwright() as p:
self.open(reverse('workspace_details', kwargs={'identifier': ws.id}), p)
self.open(reverse('workspace_details'), p)

self.page.locator("#name-edit").click()
self.page.locator("#id_name").dblclick()
Expand All @@ -65,14 +61,24 @@ def test_change_details(self):
self.page.get_by_role("button", name="Submit").click()
expect(self.page.locator("#description")).to_contain_text("other description")

def test_details_change_workspace(self):
create_workspace('other_ws', self.user)

with sync_playwright() as p:
self.open(reverse('workspace_details'), p)
expect(self.page.locator("#current_ws_name")).to_contain_text("Personal")
self.page.locator("#current_ws_name").click()
self.page.get_by_text("other_ws").click()
expect(self.page.locator("#current_ws_name")).to_contain_text("other_ws")

def test_cancel_delete(self):
other_ws = create_workspace('other_ws', self.user)
self.user.profile.current_workspace_id = other_ws.id
self.user.profile.save()

with sync_playwright() as p:
# only display one pdf
self.open(reverse('workspace_details', kwargs={'identifier': other_ws.id}), p)
self.open(reverse('workspace_details'), p)

expect(self.page.locator("#delete_workspace_modal").first).not_to_be_visible()
self.page.locator("#delete-workspace").click()
Expand All @@ -86,7 +92,7 @@ def test_delete(self):
self.user.profile.save()

with sync_playwright() as p:
self.open(reverse('workspace_details', kwargs={'identifier': other_ws.id}), p)
self.open(reverse('workspace_details'), p)

self.page.locator("#delete-workspace").click()
self.page.locator("#confirm_delete").get_by_text("Submit").click()
Expand All @@ -96,6 +102,6 @@ def test_delete(self):

def test_delete_not_visible_personal(self):
with sync_playwright() as p:
self.open(reverse('workspace_details', kwargs={'identifier': self.user.id}), p)
self.open(reverse('workspace_details'), p)

expect(self.page.locator("#delete-workspace").first).not_to_be_visible()
25 changes: 25 additions & 0 deletions pdfding/pdf/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from django.contrib.auth.hashers import check_password, make_password
from django.core.exceptions import ValidationError
from django.core.files import File
from pdf.models.collection_models import Collection
from pdf.models.pdf_models import Pdf
from pdf.models.shared_pdf_models import SharedPdf
from pdf.models.workspace_models import Workspace
Expand Down Expand Up @@ -535,6 +536,30 @@ def clean_name(self) -> str:
return collection_name


class CollectionNameForm(forms.ModelForm):
"""Form for changing the name of a collection."""

class Meta:
model = Collection
fields = ['name']

def clean_name(self) -> str: # pragma: no cover
"""Clean the submitted collection name. Removes trailing and multiple whitespaces."""

collection_name = CleanHelpers.clean_workspace_name(self.cleaned_data['name'])

return collection_name


class CollectionDescriptionForm(forms.ModelForm):
"""Form for changing the description of a collection."""

class Meta:
model = Collection
widgets = {'description': forms.Textarea(attrs={'rows': 3})}
fields = ['description']


class CleanHelpers:
@staticmethod
def clean_file(file: File) -> File:
Expand Down
52 changes: 52 additions & 0 deletions pdfding/pdf/services/collection_services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from shutil import move

from core.settings import MEDIA_ROOT
from pdf.models.collection_models import Collection
from pdf.models.pdf_models import Pdf


def move_collection(collection: Collection) -> None:
"""
Change the name of a collection and rename the collection directory on file system
accordingly. Also handle all PDFs and shared PDFs.
"""

# The new name is already set to the collection object by the form but not saved yet.
new_collection_name = collection.name.lower()
old_collection_name = Collection.objects.get(id=collection.id).name.lower()

# move all files
old_collection_path = MEDIA_ROOT / collection.workspace.id / old_collection_name
new_collection_path = MEDIA_ROOT / collection.workspace.id / new_collection_name
move(old_collection_path, new_collection_path)

# adjust the file paths of the pdf and shared pdf objects
for pdf in collection.pdfs:
adjust_pdf_path(pdf, f'/{old_collection_name}/', f'/{new_collection_name}/')

collection.save()


def adjust_pdf_path(pdf: Pdf, to_be_replaced: str, replace_with: str) -> None:
"""Adjust path of PDF and its shared PDFs when the path of a collection is changed"""

old_pdf_file_name = pdf.file.name
new_pdf_file_name = old_pdf_file_name.replace(to_be_replaced, replace_with, 1)
pdf.file.name = new_pdf_file_name

old_preview_file_name = pdf.preview.name
new_preview_file_name = old_preview_file_name.replace(to_be_replaced, replace_with, 1)
pdf.preview.name = new_preview_file_name

old_thumbnail_file_name = pdf.thumbnail.name
new_thumbnail_file_name = old_thumbnail_file_name.replace(to_be_replaced, replace_with, 1)
pdf.thumbnail.name = new_thumbnail_file_name

pdf.save()

for shared_pdf in pdf.sharedpdf_set.all():
old_qr_file_name = shared_pdf.file.name
new_qr_file_name = old_qr_file_name.replace(to_be_replaced, replace_with, 1)

shared_pdf.file.name = new_qr_file_name
shared_pdf.save()
111 changes: 111 additions & 0 deletions pdfding/pdf/templates/collection_details.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
{% extends 'layouts/blank.html' %}

{% block content %}
<div x-data="{ show_delete_workspace_modal: false }">
<div class="flex flex-col md:flex-row md:justify-end"
:class="{ 'opacity-15': show_delete_workspace_modal }">
<div class="w-full! md:w-72! lg:w-72! px-2 md:px-4 pt-2">
{% include 'includes/workspace_sidebar.html' with page='collection_details' %}
</div>
<div class="flex w-full justify-start items-center py-2 px-2 md:px-8">
<div class="rounded-md w-full min-[1200px]:w-3xl! md:ml-10 min-[1600px]:ml-40! px-4 py-4 md:pb-4 border
bg-slate-100 dark:bg-slate-800 creme:bg-creme-dark-light
border-slate-300 dark:border-slate-700 creme:border-creme-dark">
<div>
<div class="text-2xl font-bold">
Collection Details
</div>
<div class="pt-4">
<span class="text-lg font-bold">Name</span>
</div>
<div class="flex justify-between text-slate-600 dark:text-slate-400 creme:text-stone-500">
<div class="w-[86%] truncate">
<span id="name">
{{ collection.name }}
</span>
</div>
<div class="pr-0 md:pr-4">
<a id="name-edit" class="cursor-pointer text-primary hover:text-secondary"
hx-get="{% url 'edit_collection' identifier=collection.id field_name='name' %}"
hx-target="#name"
hx-swap="innerHTML">
Edit
</a>
</div>
</div>
<div class="pt-2">
<span class="text-lg font-bold">Default Collection</span>
</div>
<div class="flex justify-between text-slate-600 dark:text-slate-400 creme:text-stone-500">
<div class="w-[86%]">
<span id="default_collection">
{% if collection.default_collection %}
Yes
{% else %}
No
{% endif%}
</span>
</div>
</div>
<div class="pt-2">
<span class="text-lg font-bold">Description</span>
</div>
<div class="flex justify-between text-slate-600 dark:text-slate-400 creme:text-stone-500">
<div class="w-[86%] text-sm">
<span id="description">
{% if collection.description %}
{{ collection.description }}
{% else %}
no description available
{% endif%}
</span>
</div>
<div class="pr-0 md:pr-4">
<a id="description-edit" class="cursor-pointer text-primary hover:text-secondary"
hx-get="{% url 'edit_collection' identifier=collection.id field_name='description' %}"
hx-target="#description"
hx-swap="innerHTML">
Edit
</a>
</div>
</div>
<div class="pt-2">
<span class="text-lg font-bold">Date added</span>
</div>
<div class="flex justify-between text-slate-600 dark:text-slate-400 creme:text-stone-500">
<div class="w-[86%] text-sm">
<span id="creation_date">{{ collection.creation_date }}</span>
</div>
</div>
{% if not collection.default_collection %}
<div class="pt-12 text-xl font-bold text-red-500">Danger Zone</div>
<div class="pt-3">
<span class="text-lg font-bold">Delete Account</span>
</div>
<div class="flex justify-between text-slate-600 dark:text-slate-400 creme:text-stone-500">
<div class="w-5/6">
<span>Delete the collection and all its PDFs</span>
</div>
<div class="pr-0 ml-2 md:pl-0 md:pr-4">
<a id="delete-collection" class="text-red-500 hover:text-red-600 cursor-pointer"
@click="show_delete_delete_modal = true"
hx-get="{% url 'delete_workspace' collection.id %}"
hx-target="#delete_collection_modal"
hx-swap="innerHTML">
Delete
</a>
</div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
<div x-show="show_delete_collection_modal" x-cloak
@click.away="show_delete_collection_modal = false"
@keyup.escape.window="show_delete_collection_modal = false"
class="fixed inset-x-0 top-40 mx-auto my-auto w-80! lg:w-[28rem]! z-20">
<div id="delete_collection_modal"></div>
</div>
</div>
{% endblock %}
36 changes: 36 additions & 0 deletions pdfding/pdf/templates/includes/shared_details_sidebar.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{% extends 'layouts/base_sidebar.html' %}
{% block sidebar_content %}
<div class="flex flex-row w-full justify-between! md:flex-col gap-y-1
[&>*]:font-semibold [&>div]:px-3 md:[&>div]:px-2 [&>div]:py-2 md:[&>div]:py-1 [&>div]:rounded-md
[&>div>a]:flex [&>div>a]:flex-row [&>div>a]:items-center [&>div>a]:gap-x-2
[&>div]:hover:bg-slate-200 dark:[&>div]:hover:bg-slate-700 creme:[&>div]:hover:bg-creme-dark">
<div>
<a href="{% url 'pdf_overview' %}">
<svg class="w-5 h-5" fill="currentColor" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400.004 400.004" xml:space="preserve">
<!-- source: https://www.svgrepo.com/svg/97894/left-arrow -->
<!-- license: CC0 License-->
<path d="M382.688,182.686H59.116l77.209-77.214c6.764-6.76,6.764-17.726,0-24.485c-6.764-6.764-17.73-6.764-24.484,0L5.073,187.757
c-6.764,6.76-6.764,17.727,0,24.485l106.768,106.775c3.381,3.383,7.812,5.072,12.242,5.072c4.43,0,8.861-1.689,12.242-5.072
c6.764-6.76,6.764-17.726,0-24.484l-77.209-77.218h323.572c9.562,0,17.316-7.753,17.316-17.315
C400.004,190.438,392.251,182.686,382.688,182.686z"/>
</svg>
<span class="hidden md:block">Back to App</span>
</a>
</div>
<div {% if page == 'shared_details' %} class="bg-slate-200 md:bg-slate-100 dark:bg-slate-800 creme:bg-creme-dark-light" {% endif %}>
<a href="{% url 'shared_pdf_details' shared_pdf.id %}">
<svg fill="currentColor" class="w-5 h-5" version="1.1" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 330 330" xml:space="preserve">
<!-- source: https://www.svgrepo.com/svg/65094/information -->
<!-- license: CC0 License-->
<g>
<path d="M165,0C74.019,0,0,74.02,0,165.001C0,255.982,74.019,330,165,330s165-74.018,165-164.999C330,74.02,255.981,0,165,0z M165,300c-74.44,0-135-60.56-135-134.999C30,90.562,90.56,30,165,30s135,60.562,135,135.001C300,239.44,239.439,300,165,300z"/>
<path d="M164.998,70c-11.026,0-19.996,8.976-19.996,20.009c0,11.023,8.97,19.991,19.996,19.991 c11.026,0,19.996-8.968,19.996-19.991C184.994,78.976,176.024,70,164.998,70z"/>
<path d="M165,140c-8.284,0-15,6.716-15,15v90c0,8.284,6.716,15,15,15c8.284,0,15-6.716,15-15v-90C180,146.716,173.284,140,165,140z"/>
</g>
</svg>
<span class="hidden md:block">Details</span>
</a>
</div>
</div>
{% endblock %}
4 changes: 3 additions & 1 deletion pdfding/pdf/templates/includes/sidebar.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
/>
</form>
<div class="flex flex-row md:flex-col w-full md:py-3 gap-2 md:border-y border-slate-300 dark:border-slate-600 creme:border-stone-400">
{% include 'partials/workspace_dropdown.html' with page='pdf_overview' %}
{% include 'partials/workspace_dropdown.html' with page='overview' %}
{% if current_collection_id %}
{% include 'partials/collection_dropdown.html' %}
{% endif %}
</div>
<div class="pt-2 mt-1 ">
<div class="flex flex-row w-full justify-between! md:flex-col gap-y-1
Expand Down
Loading