mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-04-02 13:45:10 -05:00
fixes #153, adds option for inline attachments and filename filters
This commit is contained in:
parent
6e84668884
commit
e107d5df6f
@ -12,6 +12,7 @@ class MailAccountAdmin(admin.ModelAdmin):
|
||||
class MailRuleAdmin(admin.ModelAdmin):
|
||||
|
||||
radio_fields = {
|
||||
"attachment_type": admin.VERTICAL,
|
||||
"action": admin.VERTICAL,
|
||||
"assign_title_from": admin.VERTICAL,
|
||||
"assign_correspondent_from": admin.VERTICAL
|
||||
@ -29,7 +30,9 @@ class MailRuleAdmin(admin.ModelAdmin):
|
||||
('filter_from',
|
||||
'filter_subject',
|
||||
'filter_body',
|
||||
'maximum_age')
|
||||
'filter_attachment_filename',
|
||||
'maximum_age',
|
||||
'attachment_type')
|
||||
}),
|
||||
(_("Actions"), {
|
||||
'description':
|
||||
|
@ -1,6 +1,7 @@
|
||||
import os
|
||||
import tempfile
|
||||
from datetime import timedelta, date
|
||||
from fnmatch import fnmatch
|
||||
|
||||
import magic
|
||||
import pathvalidate
|
||||
@ -263,7 +264,7 @@ class MailAccountHandler(LoggingMixin):
|
||||
|
||||
for att in message.attachments:
|
||||
|
||||
if not att.content_disposition == "attachment":
|
||||
if not att.content_disposition == "attachment" and rule.attachment_type == MailRule.ATTACHMENT_TYPE_ATTACHMENTS_ONLY: # NOQA: E501
|
||||
self.log(
|
||||
'debug',
|
||||
f"Rule {rule}: "
|
||||
@ -271,6 +272,10 @@ class MailAccountHandler(LoggingMixin):
|
||||
f"with content disposition {att.content_disposition}")
|
||||
continue
|
||||
|
||||
if rule.filter_attachment_filename:
|
||||
if not fnmatch(att.filename, rule.filter_attachment_filename):
|
||||
continue
|
||||
|
||||
title = self.get_title(message, att, rule)
|
||||
|
||||
# don't trust the content type of the attachment. Could be
|
||||
|
23
src/paperless_mail/migrations/0007_auto_20210106_0138.py
Normal file
23
src/paperless_mail/migrations/0007_auto_20210106_0138.py
Normal file
@ -0,0 +1,23 @@
|
||||
# Generated by Django 3.1.5 on 2021-01-06 01:38
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('paperless_mail', '0006_auto_20210101_2340'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='mailrule',
|
||||
name='attachment_type',
|
||||
field=models.PositiveIntegerField(choices=[(1, 'Only process attachments.'), (2, "Process all files, including 'inline' attachments.")], default=1, help_text="Inline attachments include embedded images, so it's best to combine this option with a filename filter.", verbose_name='attachment type'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='mailrule',
|
||||
name='filter_attachment_filename',
|
||||
field=models.CharField(blank=True, help_text='Only consume documents which entirely match this filename if specified. Wildcards such as *.pdf or *invoice* are allowed. Case insensitive.', max_length=256, null=True, verbose_name='filter attachment filename'),
|
||||
),
|
||||
]
|
@ -60,6 +60,15 @@ class MailRule(models.Model):
|
||||
verbose_name = _("mail rule")
|
||||
verbose_name_plural = _("mail rules")
|
||||
|
||||
ATTACHMENT_TYPE_ATTACHMENTS_ONLY = 1
|
||||
ATTACHMENT_TYPE_EVERYTHING = 2
|
||||
|
||||
ATTACHMENT_TYPES = (
|
||||
(ATTACHMENT_TYPE_ATTACHMENTS_ONLY, _("Only process attachments.")),
|
||||
(ATTACHMENT_TYPE_EVERYTHING, _("Process all files, including 'inline' "
|
||||
"attachments."))
|
||||
)
|
||||
|
||||
ACTION_DELETE = 1
|
||||
ACTION_MOVE = 2
|
||||
ACTION_MARK_READ = 3
|
||||
@ -125,11 +134,27 @@ class MailRule(models.Model):
|
||||
_("filter body"),
|
||||
max_length=256, null=True, blank=True)
|
||||
|
||||
filter_attachment_filename = models.CharField(
|
||||
_("filter attachment filename"),
|
||||
max_length=256, null=True, blank=True,
|
||||
help_text=_("Only consume documents which entirely match this "
|
||||
"filename if specified. Wildcards such as *.pdf or "
|
||||
"*invoice* are allowed. Case insensitive.")
|
||||
)
|
||||
|
||||
maximum_age = models.PositiveIntegerField(
|
||||
_("maximum age"),
|
||||
default=30,
|
||||
help_text=_("Specified in days."))
|
||||
|
||||
attachment_type = models.PositiveIntegerField(
|
||||
_("attachment type"),
|
||||
choices=ATTACHMENT_TYPES,
|
||||
default=ATTACHMENT_TYPE_ATTACHMENTS_ONLY,
|
||||
help_text=_("Inline attachments include embedded images, so it's best "
|
||||
"to combine this option with a filename filter.")
|
||||
)
|
||||
|
||||
action = models.PositiveIntegerField(
|
||||
_("action"),
|
||||
choices=ACTIONS,
|
||||
|
@ -273,6 +273,49 @@ class TestMail(TestCase):
|
||||
args, kwargs = self.async_task.call_args
|
||||
self.assertEqual(kwargs['override_filename'], "f2.pdf")
|
||||
|
||||
def test_handle_inline_files(self):
|
||||
message = create_message()
|
||||
message.attachments = [
|
||||
create_attachment(filename="f1.pdf", content_disposition='inline'),
|
||||
create_attachment(filename="f2.pdf", content_disposition='attachment')
|
||||
]
|
||||
|
||||
account = MailAccount()
|
||||
rule = MailRule(assign_title_from=MailRule.TITLE_FROM_FILENAME, account=account, attachment_type=MailRule.ATTACHMENT_TYPE_EVERYTHING)
|
||||
|
||||
result = self.mail_account_handler.handle_message(message, rule)
|
||||
|
||||
self.assertEqual(result, 2)
|
||||
self.assertEqual(self.async_task.call_count, 2)
|
||||
|
||||
def test_filename_filter(self):
|
||||
message = create_message()
|
||||
message.attachments = [
|
||||
create_attachment(filename="f1.pdf"),
|
||||
create_attachment(filename="f2.pdf"),
|
||||
create_attachment(filename="f3.pdf"),
|
||||
create_attachment(filename="f2.png"),
|
||||
]
|
||||
|
||||
tests = [
|
||||
("*.pdf", ["f1.pdf", "f2.pdf", "f3.pdf"]),
|
||||
("f1.pdf", ["f1.pdf"]),
|
||||
("f1", []),
|
||||
("*", ["f1.pdf", "f2.pdf", "f3.pdf", "f2.png"]),
|
||||
("*.png", ["f2.png"]),
|
||||
]
|
||||
|
||||
for (pattern, matches) in tests:
|
||||
self.async_task.reset_mock()
|
||||
account = MailAccount()
|
||||
rule = MailRule(assign_title_from=MailRule.TITLE_FROM_FILENAME, account=account, filter_attachment_filename=pattern)
|
||||
|
||||
result = self.mail_account_handler.handle_message(message, rule)
|
||||
|
||||
self.assertEqual(result, len(matches))
|
||||
filenames = [a[1]['override_filename'] for a in self.async_task.call_args_list]
|
||||
self.assertCountEqual(filenames, matches)
|
||||
|
||||
def test_handle_mail_account_mark_read(self):
|
||||
|
||||
account = MailAccount.objects.create(name="test", imap_server="", username="admin", password="secret")
|
||||
|
Loading…
x
Reference in New Issue
Block a user