create inbox/outbox trash (#45372)
This commit is contained in:
parent
50619c3f8a
commit
522049a9a0
|
@ -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
|
||||
|
|
|
@ -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()
|
|
@ -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),
|
||||
),
|
||||
]
|
|
@ -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')
|
||||
|
|
|
@ -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 |
|
@ -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=' ')
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -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 %}
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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}),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue