create inbox/outbox trash (#45372)

This commit is contained in:
Emmanuel Cazenave 2020-07-21 17:07:58 +02:00
parent 50619c3f8a
commit 522049a9a0
18 changed files with 473 additions and 36 deletions

View File

@ -1 +1,2 @@
*/1 * * * * docbow /bin/systemctl status docbow > /dev/null 2>&1 && /usr/bin/docbow-manage notify > /dev/null
*/10 * * * * docbow /usr/bin/docbow-manage empty-trash > /dev/null

View File

@ -0,0 +1,20 @@
from datetime import timedelta
from django.conf import settings
from django.core.management.base import BaseCommand
from django.db import transaction
from django.utils.timezone import now
from docbow_project.docbow.models import DeletedDocument
class Command(BaseCommand):
help = 'Empty trash'
@transaction.atomic
def handle(self, *args, **kwargs):
target_date = now() - timedelta(days=settings.TRASH_DURATION)
for deleted_doc in DeletedDocument.objects.filter(soft_delete=True)\
.filter(soft_delete_date__lte=target_date):
deleted_doc.soft_delete = False
deleted_doc.save()

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-07-21 12:30
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('docbow', '0004_external_identifier'),
]
operations = [
migrations.AddField(
model_name='deleteddocument',
name='soft_delete',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='deleteddocument',
name='soft_delete_date',
field=models.DateTimeField(null=True),
),
]

View File

@ -395,6 +395,8 @@ class DeletedDocument(Model):
'''Mark a document as deleted'''
document = ForeignKey('Document')
user = ForeignKey('auth.User')
soft_delete = BooleanField(default=False)
soft_delete_date = DateTimeField(null=True)
class Meta:
verbose_name = _('deleted document')

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="mdi-restore" width="24" height="24" viewBox="0 0 24 24"><path d="M13,3A9,9 0 0,0 4,12H1L4.89,15.89L4.96,16.03L9,12H6A7,7 0 0,1 13,5A7,7 0 0,1 20,12A7,7 0 0,1 13,19C11.07,19 9.32,18.21 8.06,16.94L6.64,18.36C8.27,20 10.5,21 13,21A9,9 0 0,0 22,12A9,9 0 0,0 13,3Z" /></svg>

After

Width:  |  Height:  |  Size: 506 B

View File

@ -42,10 +42,8 @@ class OutboxCsvTable(tables.Table):
SELECT_ALL = mark_safe('<input type="checkbox" name="select-all" class="js-select-all"/>')
class OutboxTable(tables.Table):
select = tables.TemplateColumn(
'<input type="checkbox", name="select" class="js-select" value="{{ record.id }}" class="js-select"/>',
orderable=False, verbose_name=SELECT_ALL)
class OutboxBaseTable(tables.Table):
official_sender = tables.Column(accessor='sender.get_full_name',
order_by=('sender__last_name',
'sender__first_name',
@ -63,6 +61,34 @@ class OutboxTable(tables.Table):
verbose_name=_('filename_header'), orderable=False)
date = tables.TemplateColumn('{% load humantime %}{{ record.date|humantime }}',
verbose_name=_('date_header'))
class Meta:
model = models.Document
fields = ('official_sender',)
sequence = ('recipients', 'official_sender', 'real_sender', 'filetype', 'filenames', '...')
attrs = {"class": "paleblue mailbox-table refresh", "id": "outbox-table"}
empty_text = _('No message')
class OutboxTrashTable(OutboxBaseTable):
restore = tables.TemplateColumn(
template_name='docbow/outbox_restore_column.html',
orderable=False, verbose_name=_('Restore')
)
class Meta:
model = models.Document
fields = ('official_sender',)
sequence = ('recipients', 'official_sender', 'real_sender', 'filetype', 'filenames', '...')
attrs = {"class": "paleblue mailbox-table refresh", "id": "outbox-table"}
empty_text = _('No message')
class OutboxTable(OutboxBaseTable):
select = tables.TemplateColumn(
'<input type="checkbox", name="select" class="js-select" value="{{ record.id }}" class="js-select"/>',
orderable=False, verbose_name=SELECT_ALL)
delete = tables.TemplateColumn(template_name='docbow/outbox_delete_column.html',
orderable=False,
verbose_name=' ')
@ -95,10 +121,7 @@ class InboxCsvTable(tables.Table):
empty_text = _('No message')
class InboxTable(tables.Table):
select = tables.TemplateColumn(
'<input type="checkbox" class="js-select" name="select" value="{{ record.id }}"/>',
verbose_name=SELECT_ALL, orderable=False)
class InboxBaseTable(tables.Table):
seen = tables.BooleanColumn(accessor='seen', yesno=u' ,✔',
verbose_name=' ', orderable=False)
filetype = tables.Column(
@ -114,10 +137,34 @@ class InboxTable(tables.Table):
sender = tables.TemplateColumn('{{ record.sender_display }}',
order_by=('sender__last_name', 'sender__first_name', 'sender__username'),
verbose_name=_('sender_header'))
#date = tables.Column(
# accessor='date', verbose_name=_('date_header'))
date = tables.TemplateColumn('{% load humantime %}{{ record.date|humantime }}',
verbose_name=_('date_header'))
class Meta:
model = models.Document
fields = ('seen', 'filetype',)
attrs = {"class": "paleblue mailbox-table refresh", "id": "outbox-table"}
empty_text = _('No message')
class InboxTrashTable(InboxBaseTable):
restore = tables.TemplateColumn(
template_name='docbow/inbox_restore_column.html',
orderable=False, verbose_name=_('Restore')
)
class Meta:
model = models.Document
fields = ('select', 'seen', 'filetype',)
attrs = {"class": "paleblue mailbox-table refresh", "id": "inbox-table"}
empty_text = _('No message')
class InboxTable(InboxBaseTable):
select = tables.TemplateColumn(
'<input type="checkbox" class="js-select" name="select" value="{{ record.id }}"/>',
verbose_name=SELECT_ALL, orderable=False)
delete = tables.TemplateColumn(template_name='docbow/inbox_delete_column.html',
orderable=False, verbose_name=' ')

View File

@ -0,0 +1,6 @@
{% load i18n %}
<form action="{% url 'inbox-message-restore' doc_id=record.id %}"
method="post">
{% csrf_token %}
<input type="image" src="{{ STATIC_URL }}docbow/img/restore_icon_136265.svg" alt="{% trans "Restore" %}"/>
</form>

View File

@ -0,0 +1,17 @@
{% extends "docbow/mailbox_list.html" %}
{% load i18n %}
{% load render_table from django_tables2 %}
{% block main-column %}
{% block box_title %}
<h3>{% trans "Inbox Trash" %}</h3>
{% endblock %}
<div id="mailbox-table">
{% block mailbox.table %}
{% render_table table "docbow/trash_table.html" %}
{% endblock %}
</div>
{% endblock %}
{% block mailbox.url %}{% url 'inbox-message' mailbox_id='99999999' %}{% endblock %}

View File

@ -12,6 +12,7 @@
{% block main-column %}
{% block box_title %}
<h3>{% trans "Documents" %}</h3>
{% if show_trash %}<h4><a href="{{trash_url}}">{% trans "Trash" %}</a></h4>{% endif %}
{% endblock %}
<div id="mailbox-table">

View File

@ -0,0 +1,6 @@
{% load i18n %}
<form action="{% url 'outbox-message-restore' doc_id=record.id %}"
method="post">
{% csrf_token %}
<input type="image" src="{{ STATIC_URL }}/docbow/css/images/poubelle.png" alt="{% trans "Restore" %}"/>
</form>

View File

@ -0,0 +1,17 @@
{% extends "docbow/mailbox_list.html" %}
{% load i18n %}
{% load render_table from django_tables2 %}
{% block main-column %}
{% block box_title %}
<h3>{% trans "Outbox Trash" %}</h3>
{% endblock %}
<div id="mailbox-table">
{% block mailbox.table %}
{% render_table table "docbow/trash_table.html" %}
{% endblock %}
</div>
{% endblock %}
{% block mailbox.url %}{% url 'outbox-message' mailbox_id='99999999' %}{% endblock %}

View File

@ -0,0 +1,57 @@
{% extends "django_tables2/table.html" %}
{% load docbow %}
{% load i18n %}
{% load humanize %}
{% block table.tbody.row %}
<tr class="js-mailbox-row {% cycle "odd" "even" as parity %}"
data-id="{{ row.record.id }}"
>
{% for column, cell in row.items %}
<td {{ column.attrs.td.as_html }}>
{{ cell }}
{% if column.name == 'filetype' and row.record.comment %}
<span class="tooltip"><i class="fa fa-eye plus"></i>
<p>{{ row.record.comment }}</p>
</span>
{% endif %}
</td>
{% endfor %}
</tr>
{% block replies %}
{% for reply in row.record.document.replies.all %}
<tr class="{{parity }} reply">
<td class="reply" colspan="7">
<a href="{% url 'outbox-by-document-message' document_id=reply.id %}">
{% blocktrans with sender=reply|doc_real_sender date=reply.date|naturalday:"SHORT_DATE_FORMAT" time=reply.date|time:"H:i" %}Reply sent by {{ sender }} on {{ date }} at {{ time }}{% endblocktrans %}
</a>
</td>
</tr>
{% endfor %}
{% endblock %}
{% endblock %}
{% block pagination %}
<ul class="pagination">
{% if table.page.has_previous %}
{% block pagination.previous %}{{ block.super }}{% endblock pagination.previous %}
{% endif %}
{% if table.page.has_previous or table.page.has_next %}
{% block pagination.current %}{{ block.super }}{% endblock pagination.current %}
{% endif %}
{% if table.page.has_next %}
{% block pagination.next %}{{ block.super }}{% endblock pagination.next %}
{% endif %}
{% block pagination.cardinality %}
{% comment %}
Block of action buttons
Behaviour is in static/js/actions.js
{% endcomment %}
{% endblock %}
</ul>
{% endblock pagination %}

View File

@ -11,6 +11,7 @@ urlpatterns = [
# inbox
url(r'^inbox/$', docbow_project.docbow.views.inbox_view, name='inbox'),
url(r'^inbox/trash/$', docbow_project.docbow.views.inbox_trash_view, name='inbox-trash'),
url(r'^inbox_by_document/(?P<document_id>\d+)/$',
docbow_project.docbow.views.inbox_by_document,
name='inbox-by-document-message'),
@ -20,6 +21,9 @@ urlpatterns = [
url(r'^inbox/(?P<mailbox_id>\d+)/delete/$',
docbow_project.docbow.views.delete,
name='inbox-message-delete'),
url(r'^inbox/(?P<doc_id>\d+)/restore/$',
docbow_project.docbow.views.restore,
name='inbox-message-restore', kwargs={'outbox': False}),
url(r'^inbox/(?P<mailbox_id>\d+)/(?P<attached_file>\d+)/.*$',
docbow_project.docbow.views.message_attached_file,
name='inbox-message-attached-file'),
@ -32,12 +36,16 @@ urlpatterns = [
# outbox
url(r'^outbox/$', docbow_project.docbow.views.outbox_view, name='outbox'),
url(r'^outbox/trash/$', docbow_project.docbow.views.outbox_trash_view, name='outbox-trash'),
url(r'^outbox/(?P<mailbox_id>\d+)/$',
docbow_project.docbow.views.message, name='outbox-message',
kwargs={'outbox': True}),
url(r'^outbox/(?P<mailbox_id>\d+)/delete/$',
docbow_project.docbow.views.delete,
name='outbox-message-delete', kwargs={'outbox': True}),
url(r'^outbox/(?P<doc_id>\d+)/restore/$',
docbow_project.docbow.views.restore,
name='outbox-message-restore', kwargs={'outbox': True}),
url(r'^outbox/(?P<mailbox_id>\d+)/(?P<attached_file>\d+)/.*$',
docbow_project.docbow.views.message_attached_file,
name='outbox-message-attached-file', kwargs={'outbox': True}),

View File

@ -23,10 +23,12 @@ from django.contrib import messages
from django.core.mail import EmailMessage
from django.utils.translation import ugettext as _, ugettext_noop as N_, ungettext
from django.forms.forms import NON_FIELD_ERRORS
from django.utils.timezone import now
from django.views.generic.list import MultipleObjectMixin, ListView
from django.views.generic.base import View, RedirectView
from django.views.decorators.debug import sensitive_post_parameters
from django.views.decorators.cache import never_cache
from django.views.decorators.http import require_http_methods
from django.utils.encoding import force_text
from django.utils import six
@ -72,14 +74,13 @@ def get_base_document_query(request, qs, outbox):
private_filter = Q(private=False) | Q(private=True, mailboxes__owner=request.user)
if hasattr(request.user, 'delegate'):
private_filter = Q(private=False)
return qs.filter(mailboxes__in=mailboxes)\
.exclude(deleteddocument__user=request.user)\
.filter(private_filter)
return qs.filter(mailboxes__in=mailboxes).filter(private_filter)
def get_document(request, mailbox_id, outbox):
qs = get_base_document_query(request, Document.objects.all(), outbox)
return qs.filter(id=mailbox_id).distinct().get()
return qs.exclude(deleteddocument__user=request.user, deleteddocument__soft_delete=False)\
.filter(id=mailbox_id).distinct().get()
SEEN_DOCUMENT = '''SELECT COUNT(*) > 0
@ -90,15 +91,22 @@ AND docbow_seendocument.user_id = %s
def get_documents(request, qs, outbox):
qs = get_base_document_query(request, qs, outbox)
qs = get_base_document_query(request, qs, outbox).exclude(deleteddocument__user=request.user)
return qs.annotate(seen=RawSQL(SEEN_DOCUMENT, (request.user.pk,))).distinct()
def get_unseen_documents_count(request, qs, outbox):
qs = get_base_document_query(request, qs, outbox)
qs = get_base_document_query(request, qs, outbox).exclude(deleteddocument__user=request.user)
return qs.exclude(seendocument__user=request.user).distinct().count()
def get_trash_documents(request, qs, outbox):
return get_base_document_query(request, qs, outbox).filter(
deleteddocument__user=request.user,
deleteddocument__soft_delete=True
).distinct()
class Row(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
@ -490,8 +498,15 @@ def delete(request, mailbox_id, outbox=False):
if request.method == 'GET':
return render(request, 'docbow/delete.html', { 'document': document, 'back': back_pair, 'view_name': viewname })
else:
DeletedDocument.objects.get_or_create(user=request.user,
document=document)
try:
DeletedDocument.objects.get(user=request.user, document=document)
except DeletedDocument.DoesNotExist:
DeletedDocument.objects.create(
user=request.user,
document=document,
soft_delete=True,
soft_delete_date=now()
)
request.record('delete-document', 'marked document {document} as deleted', document=document)
return redirect(back_pair[0])
@ -500,6 +515,20 @@ def delete(request, mailbox_id, outbox=False):
def inbox_by_document(request, document_id):
return redirect('inbox-message', mailbox_id=document_id)
@require_http_methods(["POST"])
def restore(request, doc_id, outbox=False):
try:
deleted_doc = DeletedDocument.objects.get(
user=request.user, document__pk=doc_id, soft_delete=True
)
deleted_doc.delete()
except DeletedDocument.DoesNotExist:
pass
return_view = 'outbox-trash' if outbox else 'inbox-trash'
return redirect(return_view)
from django.contrib.auth import SESSION_KEY, BACKEND_SESSION_KEY
from django import http
@ -617,6 +646,19 @@ class MailboxQuerysetMixin(object):
return ctx
class TrashMailboxQuerysetMixin(object):
def get_queryset(self):
qs = super(TrashMailboxQuerysetMixin, self).get_queryset()
if not hasattr(self, '_qs'):
self._qs = get_trash_documents(self.request, qs, self.outbox)
return self._qs
def get_context_data(self, **kwargs):
ctx = super(TrashMailboxQuerysetMixin, self).get_context_data(**kwargs)
ctx['related_users'] = get_related_users(self.request)
return ctx
class MailboxView(ExtraContextMixin, MailboxQuerysetMixin, tables_views.SingleTableView):
model = Document
table_class = tables.MailboxTable
@ -624,6 +666,19 @@ class MailboxView(ExtraContextMixin, MailboxQuerysetMixin, tables_views.SingleTa
'per_page': app_settings.DOCBOW_MAILBOX_PER_PAGE,
}
def get_context_data(self):
context = super().get_context_data()
context['show_trash'] = settings.TRASH_DURATION > 0
return context
class TrashMailboxView(ExtraContextMixin, TrashMailboxQuerysetMixin, tables_views.SingleTableView):
model = Document
table_class = tables.MailboxTable
table_pagination = {
'per_page': app_settings.DOCBOW_MAILBOX_PER_PAGE,
}
class CSVMultipleObjectMixin(object):
mapping = ()
@ -755,7 +810,14 @@ class DeleteDocumentsView(object):
if documents:
if 'delete' in request.POST:
documents = documents.exclude(deleteddocument__user=request.user)
dd = [DeletedDocument(user=request.user, document=document) for document in documents]
dd = [
DeletedDocument(
user=request.user,
document=document,
soft_delete=True,
soft_delete_date=now()
) for document in documents
]
DeletedDocument.objects.bulk_create(dd)
return HttpResponseRedirect(request.build_absolute_uri())
@ -768,8 +830,27 @@ class InboxView(DeleteDocumentsView, DateFilterMixinView, MailboxView):
'view_name': 'inbox',
}
def get_context_data(self):
context = super().get_context_data()
context['trash_url'] = reverse('inbox-trash')
return context
inbox_view = login_required(InboxView.as_view())
class InboxTrashView(TrashMailboxView):
outbox = False
table_class = tables.InboxTrashTable
template_name = 'docbow/inbox_trash_list.html'
extra_ctx = {
'view_name': 'inbox_trash',
}
inbox_trash_view = login_required(InboxTrashView.as_view())
class OutboxView(DeleteDocumentsView, DateFilterMixinView, MailboxView):
outbox = True
table_class = tables.OutboxTable
@ -778,9 +859,29 @@ class OutboxView(DeleteDocumentsView, DateFilterMixinView, MailboxView):
'view_name': 'outbox',
}
def get_context_data(self):
context = super().get_context_data()
context['trash_url'] = reverse('outbox-trash')
return context
outbox_view = login_required(OutboxView.as_view())
class OutboxTrashView(TrashMailboxView):
outbox = True
table_class = tables.OutboxTrashTable
template_name = 'docbow/outbox_trash_list.html'
extra_ctx = {
'view_name': 'outbox_trash',
}
outbox_trash_view = login_required(OutboxTrashView.as_view())
class SendFileSelectorView(ListView):
template_name = 'docbow/send_file_selector.html'
model = FileType

View File

@ -243,6 +243,9 @@ SENDMAIL_DEBUG_MBOX = None
# If True every file of a document can be downloaded in a zip archive
ZIP_DOWNLOAD = False
# Duration of trash retention in days
TRASH_DURATION = 0
local_settings_file = os.environ.get('DOCBOW_SETTINGS_FILE', 'local_settings.py')
if os.path.exists(local_settings_file):
exec(open(local_settings_file).read())

View File

@ -8,12 +8,14 @@ from docbow_project.docbow.models import FileType
MEDIA_ROOT = tempfile.mkdtemp()
def assert_can_see_doc(app, doc, user, inbox=True):
def assert_can_see_doc(app, doc, user, inbox=True, trash=False):
app.login(user.username)
box_url = '/%s/' % ('inbox' if inbox else 'outbox')
doc_url = box_url + '%s/' % doc.pk
if trash:
box_url += 'trash/'
resp = app.get(box_url)
assert doc_url in resp.text
assert 'data-id="%s"' % doc.pk in resp.text
resp = app.get(doc_url)
assert resp.status_code == 200
app.logout()

View File

@ -2,8 +2,9 @@ from datetime import datetime, timedelta
from django.contrib.auth.models import User
from django.core import management
from django.utils.timezone import now
from docbow_project.docbow.models import Document
from docbow_project.docbow.models import Document, DeletedDocument
from utils import assert_can_see_doc, send_file
@ -65,3 +66,42 @@ def test_forward_docs_exclude_already_received_docs(app, users, settings, filety
management.call_command('forward-docs', '%s' % recipient.pk, '%s' % new_user.pk)
# Nothing done
assert Document.objects.filter(sender=sender).count() == 1
def test_empty_trash(app, users, settings, filetypes):
settings.TRASH_DURATION = 1
sender = User.objects.get(username='user-1')
recipient = User.objects.get(username='user-2')
send_file(app, settings, sender, recipient)
doc = Document.objects.get()
assert DeletedDocument.objects.count() == 0
management.call_command('empty-trash')
# Nothing done
assert DeletedDocument.objects.count() == 0
deleted_doc = DeletedDocument.objects.create(document=doc, user=recipient)
assert deleted_doc.soft_delete is False
assert deleted_doc.soft_delete_date is None
management.call_command('empty-trash')
deleted_doc.refresh_from_db()
assert deleted_doc.soft_delete is False
assert deleted_doc.soft_delete_date is None
deleted_doc.soft_delete = True
now_date = now()
deleted_doc.soft_delete_date = now_date
deleted_doc.save()
management.call_command('empty-trash')
deleted_doc.refresh_from_db()
assert deleted_doc.soft_delete is True
assert deleted_doc.soft_delete_date == now_date
days = settings.TRASH_DURATION + 1
past_obj = now() - timedelta(days=days)
deleted_doc.soft_delete_date = past_obj
deleted_doc.save()
management.call_command('empty-trash')
deleted_doc.refresh_from_db()
assert deleted_doc.soft_delete is False
assert deleted_doc.soft_delete_date == past_obj

View File

@ -176,10 +176,35 @@ def test_outbox_doc(app, filetypes, users, settings):
assert resp.status_code == 302
assert resp.location.endswith('/outbox/')
assert DeletedDocument.objects.get(user=sender, document=doc)
resp = app.get('/outbox/%s/' % doc.pk)
assert resp.status_code == 302
assert resp.location.endswith('/outbox/')
assert DeletedDocument.objects.get(user=sender, document=doc, soft_delete=True)
assert_can_see_doc(app, doc, sender, inbox=False, trash=True)
# go to trash and restore
app.login(sender.username)
resp = app.get('/outbox/trash/')
assert '/outbox/%s/restore' % doc.pk in resp.text
restore_form = resp.forms[0]
resp = restore_form.submit()
assert resp.location.endswith('/outbox/trash/')
assert_can_see_doc(app, doc, sender, inbox=False)
def test_outbox_trash_link(app, filetypes, users, settings):
sender = User.objects.get(username='user-1')
recipient = User.objects.get(username='user-2')
send_file(app, settings, sender, recipient)
Document.objects.get(sender=sender)
AttachedFile.objects.first()
app.login(sender.username)
# trash disabled by default, no link
resp = app.get('/outbox/')
assert 'Trash' not in resp.text
# enable trash
settings.TRASH_DURATION = 1
resp = app.get('/outbox/')
assert 'Trash' in resp.text
def test_inbox_doc(app, filetypes, users, settings):
@ -210,9 +235,34 @@ def test_inbox_doc(app, filetypes, users, settings):
assert resp.location.endswith('/inbox/?page=1')
assert DeletedDocument.objects.get(user=recipient, document=doc)
resp = app.get('/inbox/%s/' % doc.pk)
assert resp.status_code == 302
assert resp.location.endswith('/inbox/')
assert_can_see_doc(app, doc, recipient, inbox=True, trash=True)
# go to trash and restore
app.login(recipient.username)
resp = app.get('/inbox/trash/')
assert '/inbox/%s/restore' % doc.pk in resp.text
restore_form = resp.forms[0]
resp = restore_form.submit()
assert resp.location.endswith('/inbox/trash/')
assert_can_see_doc(app, doc, recipient)
def test_inbox_trash_link(app, filetypes, users, settings):
sender = User.objects.get(username='user-1')
recipient = User.objects.get(username='user-2')
send_file(app, settings, sender, recipient)
Document.objects.get(sender=sender)
AttachedFile.objects.first()
app.login(recipient.username)
# trash disabled by default, no link
resp = app.get('/inbox/')
assert 'Trash' not in resp.text
# enable trash
settings.TRASH_DURATION = 1
resp = app.get('/inbox/')
assert 'Trash' in resp.text
def test_inbox_unseen_doc(app, filetypes, users, settings):
@ -240,15 +290,48 @@ def test_delete_doc(app, filetypes, users, settings):
send_file(app, settings, sender, recipient)
doc = Document.objects.get(sender=sender)
assert DeletedDocument.objects.count() == 0
app.login(recipient.username)
resp = app.get('/inbox/')
assert 'data-id="%s"' % doc.pk in resp.text
resp = app.get('/inbox/%s/delete/' % doc.pk)
assert resp.status_code == 200
resp = resp.form.submit()
assert resp.status_code == 302
assert resp.location.endswith('/inbox/?page=1')
# soft delete, doc still available
resp = app.get('/inbox/%s/' % doc.pk)
assert resp.status_code == 200
# not showing up in inbox
resp = app.get('/inbox/')
assert 'data-id="%s"' % doc.pk not in resp.text
# showing up in trash
resp = app.get('/inbox/trash/')
assert 'data-id="%s"' % doc.pk in resp.text
deleted_document = DeletedDocument.objects.get(document=doc, user=recipient)
assert deleted_document.soft_delete
assert deleted_document.soft_delete_date
def test_delete_doc_through_inbox(app, filetypes, users, settings):
sender = User.objects.get(username='user-1')
recipient = User.objects.get(username='user-2')
send_file(app, settings, sender, recipient)
doc = Document.objects.get(sender=sender)
assert DeletedDocument.objects.count() == 0
app.login(recipient.username)
resp = app.get('/inbox/')
delete_form = resp.forms[2]
delete_form['selection'] = doc.pk
resp = delete_form.submit('delete')
assert resp.status_code == 302
assert resp.location.endswith('/inbox/')
# soft delete
deleted_document = DeletedDocument.objects.get(document=doc, user=recipient)
assert deleted_document.soft_delete
assert deleted_document.soft_delete_date
def test_forward_doc(app, filetypes, users, settings):
@ -361,9 +444,9 @@ def test_document_acces(app, filetypes, settings, users):
# Visibility after deletion by user
delete_doc(app, user)
assert_cannot_see_doc(app, doc, user)
assert_can_see_doc(app, doc, user, trash=True)
assert_can_see_doc(app, doc, delegate) # yes it's strange
assert_cannot_see_doc(app, doc, guest_delegate)
assert_can_see_doc(app, doc, guest_delegate, trash=True)
assert_cannot_see_doc(app, doc, stranger)
assert_can_see_doc(app, doc, sender, inbox=False)
@ -375,7 +458,7 @@ def test_document_acces(app, filetypes, settings, users):
doc = Document.objects.get(sender=sender)
delete_doc(app, delegate)
assert_can_see_doc(app, doc, user)
assert_cannot_see_doc(app, doc, delegate)
assert_can_see_doc(app, doc, delegate, trash=True)
assert_can_see_doc(app, doc, guest_delegate)
assert_can_see_doc(app, doc, sender, inbox=False)
@ -386,9 +469,9 @@ def test_document_acces(app, filetypes, settings, users):
send_file(app, settings, sender, user)
doc = Document.objects.get(sender=sender)
delete_doc(app, guest_delegate)
assert_cannot_see_doc(app, doc, user)
assert_can_see_doc(app, doc, user, trash=True)
assert_can_see_doc(app, doc, delegate)
assert_cannot_see_doc(app, doc, guest_delegate)
assert_can_see_doc(app, doc, guest_delegate, trash=True)
assert_can_see_doc(app, doc, sender, inbox=False)
@ -411,7 +494,7 @@ def test_private_document_access(app, filetypes, settings, users):
# Visibility after deletion by user
delete_doc(app, user)
assert_cannot_see_doc(app, doc, user)
assert_can_see_doc(app, doc, user, trash=True)
assert_cannot_see_doc(app, doc, delegate)
assert_cannot_see_doc(app, doc, guest_delegate)
assert_cannot_see_doc(app, doc, stranger)