diff --git a/README.rst b/README.rst index 1589666..8f8700b 100644 --- a/README.rst +++ b/README.rst @@ -55,6 +55,7 @@ Installation: 6. Configure ``FILE_UPLOAD_MAX_SIZE`` (optional). This is the maximum size in bytes before raising form validation errors. If not set there is no restriction on file size. + Mind that you serve files! ========================== @@ -68,6 +69,10 @@ configuration this would look like:: AddType text/plain .html .htm .shtml .php .php5 .php4 .pl .cgi +Private attachments (e.g. attachments marked as +``requires_watermark``) are kept in the ``private_attachments`` +folder. Make sure this is not public + Tests ===== diff --git a/attachments/forms.py b/attachments/forms.py index 8418391..981221c 100644 --- a/attachments/forms.py +++ b/attachments/forms.py @@ -28,7 +28,7 @@ class AttachmentForm(forms.ModelForm): class Meta: model = Attachment - fields = ("attachment_file", "title", "description") + fields = ("attachment_file", "title", "description", "requires_watermark") def save(self, request, obj, *args, **kwargs): self.instance.creator = request.user diff --git a/attachments/migrations/0005_requires_watermark.py b/attachments/migrations/0005_requires_watermark.py new file mode 100644 index 0000000..afc32aa --- /dev/null +++ b/attachments/migrations/0005_requires_watermark.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.8 on 2020-07-15 06:39 + +from django.db import migrations, models +import attachments.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('attachments', '0004_change_model_options'), + ] + + operations = [ + migrations.AddField( + model_name='attachment', + name='requires_watermark', + field=models.BooleanField(null=True, verbose_name='Requires Watermark'), + ), + ] diff --git a/attachments/migrations/0006_requires_watermark_assign_default.py b/attachments/migrations/0006_requires_watermark_assign_default.py new file mode 100644 index 0000000..c219db9 --- /dev/null +++ b/attachments/migrations/0006_requires_watermark_assign_default.py @@ -0,0 +1,23 @@ +# Generated by Django 3.0.8 on 2020-07-15 06:39 + +from django.db import migrations, models +import attachments.models + + +def assign_requires_watermark(apps, schema_editor): + Attachment = apps.get_model('attachments', 'Attachment') + Attachment.objects.all().update(requires_watermark=False) + + +class Migration(migrations.Migration): + + dependencies = [ + ('attachments', '0005_requires_watermark'), + ] + + operations = [ + migrations.RunPython( + code=assign_requires_watermark, + reverse_code=migrations.RunPython.noop, + ), + ] diff --git a/attachments/migrations/0007_requires_watermark_make_not_null.py b/attachments/migrations/0007_requires_watermark_make_not_null.py new file mode 100644 index 0000000..8d5912d --- /dev/null +++ b/attachments/migrations/0007_requires_watermark_make_not_null.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.8 on 2020-07-15 06:39 + +from django.db import migrations, models +import attachments.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('attachments', '0006_requires_watermark_assign_default'), + ] + + operations = [ + migrations.AlterField( + model_name='attachment', + name='requires_watermark', + field=models.BooleanField(null=False, default=False, verbose_name='Requires Watermark'), + ), + ] diff --git a/attachments/migrations/TODO-makemigrations b/attachments/migrations/TODO-makemigrations new file mode 100644 index 0000000..e69de29 diff --git a/attachments/models.py b/attachments/models.py index 0e38c56..c20c6ac 100644 --- a/attachments/models.py +++ b/attachments/models.py @@ -11,12 +11,22 @@ def attachment_upload(instance, filename): - """Stores the attachment in a "per module/appname/primary key" folder""" - return "attachments/{app}_{model}/{pk}/{filename}".format( + """ + Stores the attachment in a "per module/appname/primary key" folder + Attachments that need to be private (i.e. requires_watermark + attachments), go in the private_attachments folder + + """ + attachments_prefix = "attachments" + if instance.requires_watermark: + attachments_prefix = "private_attachments" + + return "{attachments_prefix}/{app}_{model}/{pk}/{filename}".format( app=instance.content_object._meta.app_label, model=instance.content_object._meta.object_name.lower(), pk=instance.content_object.pk, filename=filename, + attachments_prefix=attachments_prefix, ) @@ -54,6 +64,7 @@ class Attachment(models.Model): show_in_standard_package = models.BooleanField( _("Show in standard package"), default=True ) + requires_watermark = models.BooleanField(_("Requires Watermark"), default=False) class Meta: verbose_name = _("attachment") diff --git a/attachments/tests/base.py b/attachments/tests/base.py index 051d296..0de9ba7 100644 --- a/attachments/tests/base.py +++ b/attachments/tests/base.py @@ -41,10 +41,11 @@ def setUp(self): self.obj = TestModel.objects.create(title="My first test item") - def _upload_testfile(self, file_obj=None): + def _upload_testfile(self, file_obj=None, opts=None): """ Uploads a sample file for the given user. """ + opts = {} if opts == None else opts add_url = reverse( "attachments:add", kwargs={ @@ -61,5 +62,5 @@ def _upload_testfile(self, file_obj=None): content_type="image/jpeg", ) return self.client.post( - add_url, {"attachment_file": file_obj}, follow=True + add_url, {"attachment_file": file_obj, **opts}, follow=True ) diff --git a/attachments/tests/test_views.py b/attachments/tests/test_views.py index 2cb521d..983253d 100644 --- a/attachments/tests/test_views.py +++ b/attachments/tests/test_views.py @@ -223,3 +223,17 @@ def test_delete_does_not_raise_if_os_remove_raises(self): self.assertEqual(Attachment.objects.count(), 0) # NOTE: we don't assert the file path here because # the mock which raises will not actually delete it + + def test_requires_watermark_uploads_to_private_location(self): + self.client.login(**self.cred_jon) + self._upload_testfile(file_obj=None, opts={'requires_watermark': True}) + self.assertEqual(Attachment.objects.count(), 1) + att = Attachment.objects.attachments_for_object(self.obj) + self.assertTrue(att[0].attachment_file.name.startswith('private_attachments')) + + def test_without_requires_watermark_uploads_to_standard_location(self): + self.client.login(**self.cred_jon) + self._upload_testfile(file_obj=None, opts={'requires_watermark': False}) + self.assertEqual(Attachment.objects.count(), 1) + att = Attachment.objects.attachments_for_object(self.obj) + self.assertTrue(att[0].attachment_file.name.startswith('attachments'))