Browse Source

Bump data download file field length to 255, add expire and delete actions to downloads admin

Rafał Pitoń 7 years ago
parent
commit
1ca643493b

+ 8 - 0
misago/users/datadownload.py

@@ -17,3 +17,11 @@ def prepare_user_data_download(user, requester=None):
         requester=requester,
         requester_name=requester.username,
     )
+
+
+def expire_user_data_download(download):
+    download.status = DataDownload.STATUS_EXPIRED
+    if download.file:
+        download.file.delete(save=False)
+    download.save()
+    

+ 1 - 1
misago/users/migrations/0014_datadownload.py

@@ -24,7 +24,7 @@ class Migration(migrations.Migration):
                 ('requester_name', models.CharField(max_length=255)),
                 ('requested_on', models.DateTimeField(default=django.utils.timezone.now)),
                 ('expires_on', models.DateTimeField(default=django.utils.timezone.now)),
-                ('file', models.FileField(blank=True, null=True, upload_to=misago.users.models.datadownload.get_data_upload_to)),
+                ('file', models.FileField(blank=True, max_length=255, null=True, upload_to=misago.users.models.datadownload.get_data_upload_to)),
                 ('requester', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
                 ('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
             ],

+ 12 - 2
misago/users/models/datadownload.py

@@ -1,11 +1,16 @@
+from hashlib import md5
+
 from django.conf import settings
 from django.db import models
 from django.utils import timezone
+from django.utils.crypto import get_random_string
 from django.utils.translation import ugettext_lazy as _
 
 
 def get_data_upload_to(instance, filename):
-    raise NotImplementedError() # todo: generate secure upload to path
+    user_id_hexdigest = md5(str(instance.user_id).encode()).hexdigest()
+    return 'data-downloads/{}/{}/{}.zip'.format(
+        user_id_hexdigest, get_random_string(64), instance.user.slug)
 
 
 class DataDownload(models.Model):
@@ -37,7 +42,12 @@ class DataDownload(models.Model):
     requester_name = models.CharField(max_length=255)
     requested_on = models.DateTimeField(default=timezone.now)
     expires_on = models.DateTimeField(default=timezone.now)
-    file = models.FileField(upload_to=get_data_upload_to, null=True, blank=True)
+    file = models.FileField(upload_to=get_data_upload_to, max_length=255, null=True, blank=True)
 
     class Meta:
         ordering = ['-pk']
+
+    def delete(self, *args, **kwargs):
+        if self.file:
+            self.file.delete(save=False)
+        super(DataDownload, self).delete(*args, **kwargs)

+ 84 - 0
misago/users/tests/test_datadownloadadmin_views.py

@@ -0,0 +1,84 @@
+import os
+
+from django.core.files import File
+from django.urls import reverse
+
+from misago.admin.testutils import AdminTestCase
+from misago.users.datadownload import prepare_user_data_download
+from misago.users.models import DataDownload
+
+
+TESTFILES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'testfiles')
+TEST_FILE_PATH = os.path.join(TESTFILES_DIR, 'avatar.png')
+
+
+class DataDownloadAdminViewsTests(AdminTestCase):
+    def test_link_registered(self):
+        """admin nav contains data downloads link"""
+        response = self.client.get(reverse('misago:admin:users:accounts:index'))
+
+        response = self.client.get(response['location'])
+        self.assertContains(response, reverse('misago:admin:users:data-downloads:index'))
+
+    def test_list_view(self):
+        """data downloads list view returns 200"""
+        response = self.client.get(reverse('misago:admin:users:data-downloads:index'))
+        self.assertEqual(response.status_code, 302)
+
+        view_url = response['location']
+
+        response = self.client.get(view_url)
+        self.assertEqual(response.status_code, 200)
+
+        prepare_user_data_download(self.user)
+        response = self.client.get(view_url)
+        self.assertEqual(response.status_code, 200)
+
+    def test_expire_action(self):
+        """expire action marks data download as expired and deletes its file"""
+        data_download = prepare_user_data_download(self.user)
+
+        with open(TEST_FILE_PATH, 'rb') as upload:
+            data_download.file = File(upload)
+            data_download.save()
+
+        self.assertIsNotNone(data_download.file)
+        self.assertTrue(os.path.isfile(data_download.file.path))
+
+        response = self.client.post(
+            reverse('misago:admin:users:data-downloads:index'),
+            data={
+                'action': 'expire',
+                'selected_items': [data_download.pk],
+            }
+        )
+        self.assertEqual(response.status_code, 302)
+
+        updated_download = DataDownload.objects.get(pk=data_download.pk)
+        self.assertEqual(updated_download.status, DataDownload.STATUS_EXPIRED)
+        self.assertFalse(updated_download.file)
+
+        self.assertFalse(os.path.isfile(data_download.file.path))
+
+    def test_delete_action(self):
+        """dele action deletes data download together with its file"""
+        data_download = prepare_user_data_download(self.user)
+
+        with open(TEST_FILE_PATH, 'rb') as upload:
+            data_download.file = File(upload)
+            data_download.save()
+
+        self.assertIsNotNone(data_download.file)
+        self.assertTrue(os.path.isfile(data_download.file.path))
+
+        response = self.client.post(
+            reverse('misago:admin:users:data-downloads:index'),
+            data={
+                'action': 'delete',
+                'selected_items': [data_download.pk],
+            }
+        )
+        self.assertEqual(response.status_code, 302)
+
+        self.assertEqual(DataDownload.objects.count(), 0)
+        self.assertFalse(os.path.isfile(data_download.file.path))

+ 31 - 1
misago/users/views/admin/datadownloads.py

@@ -1,6 +1,8 @@
+from django.contrib import messages
 from django.utils.translation import ugettext_lazy as _
 
 from misago.admin.views import generic
+from misago.users.datadownload import expire_user_data_download
 from misago.users.forms.admin import SearchDataDownloadsForm
 from misago.users.models import DataDownload
 
@@ -17,10 +19,38 @@ class DataDownloadsList(DataDownloadAdmin, generic.ListView):
         ('-id', _("From newest")),
         ('id', _("From oldest")),
     ]
+    selection_label = _('With data downloads: 0')
+    empty_selection_label = _('Select data downloads')
+    mass_actions = [
+        {
+            'action': 'expire',
+            'name': _("Expire downloads"),
+            'icon': 'fa fa-ban',
+            'confirmation': _("Are you sure you want to set selected data downloads as expired?"),
+        },
+        {
+            'action': 'delete',
+            'name': _("Delete downloads"),
+            'icon': 'fa fa-times-circle',
+            'confirmation': _("Are you sure you want to delete selected data downloads?"),
+        },
+    ]
 
     def get_queryset(self):
         qs = super(DataDownloadsList, self).get_queryset()
         return qs.select_related('user', 'requester')
         
     def get_search_form(self, request):
-        return SearchDataDownloadsForm
+        return SearchDataDownloadsForm
+
+    def action_expire(self, request, data_downloads):
+        for data_download in data_downloads:
+            expire_user_data_download(data_download)
+
+        messages.success(request, _("Selected data downloads have been set as expired."))
+
+    def action_delete(self, request, data_downloads):
+        for data_download in data_downloads:
+            data_download.delete()
+
+        messages.success(request, _("Selected data downloads have been deleted."))