Feature: customizable fields display for documents, saved views & dashboard widgets (#6439)

This commit is contained in:
shamoon
2024-04-26 06:41:12 -07:00
committed by GitHub
parent 7a0334f353
commit bd4476d484
50 changed files with 2929 additions and 1018 deletions

View File

@@ -0,0 +1,49 @@
# Generated by Django 4.2.11 on 2024-04-16 18:35
import django.core.validators
from django.db import migrations
from django.db import models
class Migration(migrations.Migration):
dependencies = [
("documents", "1046_workflowaction_remove_all_correspondents_and_more"),
]
operations = [
migrations.AddField(
model_name="savedview",
name="display_mode",
field=models.CharField(
blank=True,
choices=[
("table", "Table"),
("smallCards", "Small Cards"),
("largeCards", "Large Cards"),
],
max_length=128,
null=True,
verbose_name="View display mode",
),
),
migrations.AddField(
model_name="savedview",
name="page_size",
field=models.PositiveIntegerField(
blank=True,
null=True,
validators=[django.core.validators.MinValueValidator(1)],
verbose_name="View page size",
),
),
migrations.AddField(
model_name="savedview",
name="display_fields",
field=models.JSONField(
blank=True,
null=True,
verbose_name="Document display fields",
),
),
]

View File

@@ -394,6 +394,25 @@ class Log(models.Model):
class SavedView(ModelWithOwner):
class DisplayMode(models.TextChoices):
TABLE = ("table", _("Table"))
SMALL_CARDS = ("smallCards", _("Small Cards"))
LARGE_CARDS = ("largeCards", _("Large Cards"))
class DisplayFields(models.TextChoices):
TITLE = ("title", _("Title"))
CREATED = ("created", _("Created"))
ADDED = ("added", _("Added"))
TAGS = ("tag"), _("Tags")
CORRESPONDENT = ("correspondent", _("Correspondent"))
DOCUMENT_TYPE = ("documenttype", _("Document Type"))
STORAGE_PATH = ("storagepath", _("Storage Path"))
NOTES = ("note", _("Note"))
OWNER = ("owner", _("Owner"))
SHARED = ("shared", _("Shared"))
ASN = ("asn", _("ASN"))
CUSTOM_FIELD = ("custom_field_%d", ("Custom Field"))
name = models.CharField(_("name"), max_length=128)
show_on_dashboard = models.BooleanField(
@@ -411,6 +430,27 @@ class SavedView(ModelWithOwner):
)
sort_reverse = models.BooleanField(_("sort reverse"), default=False)
page_size = models.PositiveIntegerField(
_("View page size"),
null=True,
blank=True,
validators=[MinValueValidator(1)],
)
display_mode = models.CharField(
max_length=128,
verbose_name=_("View display mode"),
choices=DisplayMode.choices,
null=True,
blank=True,
)
display_fields = models.JSONField(
verbose_name=_("Document display fields"),
null=True,
blank=True,
)
class Meta:
ordering = ("name",)
verbose_name = _("saved view")

View File

@@ -815,12 +815,33 @@ class SavedViewSerializer(OwnedObjectSerializer):
"sort_field",
"sort_reverse",
"filter_rules",
"page_size",
"display_mode",
"display_fields",
"owner",
"permissions",
"user_can_change",
"set_permissions",
]
def validate(self, attrs):
attrs = super().validate(attrs)
if "display_fields" in attrs and attrs["display_fields"] is not None:
for field in attrs["display_fields"]:
if (
SavedView.DisplayFields.CUSTOM_FIELD[:-2] in field
): # i.e. check for 'custom_field_' prefix
field_id = int(re.search(r"\d+", field)[0])
if not CustomField.objects.filter(id=field_id).exists():
raise serializers.ValidationError(
f"Invalid field: {field}",
)
elif field not in SavedView.DisplayFields.values:
raise serializers.ValidationError(
f"Invalid field: {field}",
)
return attrs
def update(self, instance, validated_data):
if "filter_rules" in validated_data:
rules_data = validated_data.pop("filter_rules")

View File

@@ -1614,7 +1614,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
status.HTTP_404_NOT_FOUND,
)
def test_create_update_patch(self):
def test_saved_view_create_update_patch(self):
User.objects.create_user("user1")
view = {
@@ -1661,6 +1661,155 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
v1 = SavedView.objects.get(id=v1.id)
self.assertEqual(v1.filter_rules.count(), 0)
def test_saved_view_display_options(self):
"""
GIVEN:
- Saved view
WHEN:
- Updating display options
THEN:
- Display options are updated
- Display fields are validated
"""
User.objects.create_user("user1")
view = {
"name": "test",
"show_on_dashboard": True,
"show_in_sidebar": True,
"sort_field": "created2",
"filter_rules": [{"rule_type": 4, "value": "test"}],
"page_size": 20,
"display_mode": SavedView.DisplayMode.SMALL_CARDS,
"display_fields": [
SavedView.DisplayFields.TITLE,
SavedView.DisplayFields.CREATED,
],
}
response = self.client.post("/api/saved_views/", view, format="json")
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
v1 = SavedView.objects.get(name="test")
self.assertEqual(v1.page_size, 20)
self.assertEqual(
v1.display_mode,
SavedView.DisplayMode.SMALL_CARDS,
)
self.assertEqual(
v1.display_fields,
[
SavedView.DisplayFields.TITLE,
SavedView.DisplayFields.CREATED,
],
)
response = self.client.patch(
f"/api/saved_views/{v1.id}/",
{
"display_fields": [
SavedView.DisplayFields.TAGS,
SavedView.DisplayFields.TITLE,
SavedView.DisplayFields.CREATED,
],
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
v1.refresh_from_db()
self.assertEqual(
v1.display_fields,
[
SavedView.DisplayFields.TAGS,
SavedView.DisplayFields.TITLE,
SavedView.DisplayFields.CREATED,
],
)
# Invalid display field
response = self.client.patch(
f"/api/saved_views/{v1.id}/",
{
"display_fields": [
"foobar",
],
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_saved_view_display_customfields(self):
"""
GIVEN:
- Saved view
WHEN:
- Updating display options with custom fields
THEN:
- Display filds for custom fields are updated
- Display fields for custom fields are validated
"""
view = {
"name": "test",
"show_on_dashboard": True,
"show_in_sidebar": True,
"sort_field": "created2",
"filter_rules": [{"rule_type": 4, "value": "test"}],
"page_size": 20,
"display_mode": SavedView.DisplayMode.SMALL_CARDS,
"display_fields": [
SavedView.DisplayFields.TITLE,
SavedView.DisplayFields.CREATED,
],
}
response = self.client.post("/api/saved_views/", view, format="json")
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
v1 = SavedView.objects.get(name="test")
custom_field = CustomField.objects.create(
name="stringfield",
data_type=CustomField.FieldDataType.STRING,
)
response = self.client.patch(
f"/api/saved_views/{v1.id}/",
{
"display_fields": [
SavedView.DisplayFields.TITLE,
SavedView.DisplayFields.CREATED,
SavedView.DisplayFields.CUSTOM_FIELD % custom_field.id,
],
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
v1.refresh_from_db()
self.assertEqual(
v1.display_fields,
[
str(SavedView.DisplayFields.TITLE),
str(SavedView.DisplayFields.CREATED),
SavedView.DisplayFields.CUSTOM_FIELD % custom_field.id,
],
)
# Custom field not found
response = self.client.patch(
f"/api/saved_views/{v1.id}/",
{
"display_fields": [
SavedView.DisplayFields.TITLE,
SavedView.DisplayFields.CREATED,
SavedView.DisplayFields.CUSTOM_FIELD % 99,
],
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_get_logs(self):
log_data = "test\ntest2\n"
with open(os.path.join(settings.LOGGING_DIR, "mail.log"), "w") as f:

View File

@@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-19 01:15-0700\n"
"POT-Creation-Date: 2024-04-24 22:54-0700\n"
"PO-Revision-Date: 2022-02-17 04:17\n"
"Last-Translator: \n"
"Language-Team: English\n"
@@ -21,31 +21,31 @@ msgstr ""
msgid "Documents"
msgstr ""
#: documents/models.py:36 documents/models.py:739
#: documents/models.py:36 documents/models.py:779
msgid "owner"
msgstr ""
#: documents/models.py:53 documents/models.py:902
#: documents/models.py:53 documents/models.py:942
msgid "None"
msgstr ""
#: documents/models.py:54 documents/models.py:903
#: documents/models.py:54 documents/models.py:943
msgid "Any word"
msgstr ""
#: documents/models.py:55 documents/models.py:904
#: documents/models.py:55 documents/models.py:944
msgid "All words"
msgstr ""
#: documents/models.py:56 documents/models.py:905
#: documents/models.py:56 documents/models.py:945
msgid "Exact match"
msgstr ""
#: documents/models.py:57 documents/models.py:906
#: documents/models.py:57 documents/models.py:946
msgid "Regular expression"
msgstr ""
#: documents/models.py:58 documents/models.py:907
#: documents/models.py:58 documents/models.py:947
msgid "Fuzzy word"
msgstr ""
@@ -53,20 +53,20 @@ msgstr ""
msgid "Automatic"
msgstr ""
#: documents/models.py:62 documents/models.py:397 documents/models.py:1223
#: documents/models.py:62 documents/models.py:416 documents/models.py:1263
#: paperless_mail/models.py:18 paperless_mail/models.py:93
msgid "name"
msgstr ""
#: documents/models.py:64 documents/models.py:963
#: documents/models.py:64 documents/models.py:1003
msgid "match"
msgstr ""
#: documents/models.py:67 documents/models.py:966
#: documents/models.py:67 documents/models.py:1006
msgid "matching algorithm"
msgstr ""
#: documents/models.py:72 documents/models.py:971
#: documents/models.py:72 documents/models.py:1011
msgid "is insensitive"
msgstr ""
@@ -132,7 +132,7 @@ msgstr ""
msgid "title"
msgstr ""
#: documents/models.py:171 documents/models.py:653
#: documents/models.py:171 documents/models.py:693
msgid "content"
msgstr ""
@@ -162,8 +162,8 @@ msgstr ""
msgid "The checksum of the archived document."
msgstr ""
#: documents/models.py:205 documents/models.py:385 documents/models.py:659
#: documents/models.py:697 documents/models.py:767 documents/models.py:804
#: documents/models.py:205 documents/models.py:385 documents/models.py:699
#: documents/models.py:737 documents/models.py:807 documents/models.py:844
msgid "created"
msgstr ""
@@ -211,7 +211,7 @@ msgstr ""
msgid "The position of this document in your physical document archive."
msgstr ""
#: documents/models.py:279 documents/models.py:670 documents/models.py:724
#: documents/models.py:279 documents/models.py:710 documents/models.py:764
msgid "document"
msgstr ""
@@ -259,584 +259,652 @@ msgstr ""
msgid "logs"
msgstr ""
#: documents/models.py:398
msgid "Table"
msgstr ""
#: documents/models.py:399
msgid "Small Cards"
msgstr ""
#: documents/models.py:400
msgid "show on dashboard"
msgid "Large Cards"
msgstr ""
#: documents/models.py:403
msgid "show in sidebar"
msgid "Title"
msgstr ""
#: documents/models.py:404
msgid "Created"
msgstr ""
#: documents/models.py:405
msgid "Added"
msgstr ""
#: documents/models.py:406
msgid "Tags"
msgstr ""
#: documents/models.py:407
msgid "sort field"
msgid "Correspondent"
msgstr ""
#: documents/models.py:408
msgid "Document Type"
msgstr ""
#: documents/models.py:409
msgid "Storage Path"
msgstr ""
#: documents/models.py:410
msgid "Note"
msgstr ""
#: documents/models.py:411
msgid "Owner"
msgstr ""
#: documents/models.py:412
msgid "sort reverse"
msgid "Shared"
msgstr ""
#: documents/models.py:416 documents/models.py:469
msgid "saved view"
#: documents/models.py:413
msgid "ASN"
msgstr ""
#: documents/models.py:417
msgid "saved views"
#: documents/models.py:419
msgid "show on dashboard"
msgstr ""
#: documents/models.py:425
msgid "title contains"
#: documents/models.py:422
msgid "show in sidebar"
msgstr ""
#: documents/models.py:426
msgid "content contains"
msgstr ""
#: documents/models.py:427
msgid "ASN is"
msgstr ""
#: documents/models.py:428
msgid "correspondent is"
msgstr ""
#: documents/models.py:429
msgid "document type is"
msgstr ""
#: documents/models.py:430
msgid "is in inbox"
msgid "sort field"
msgstr ""
#: documents/models.py:431
msgid "has tag"
msgstr ""
#: documents/models.py:432
msgid "has any tag"
msgstr ""
#: documents/models.py:433
msgid "created before"
msgid "sort reverse"
msgstr ""
#: documents/models.py:434
msgid "created after"
msgstr ""
#: documents/models.py:435
msgid "created year is"
msgstr ""
#: documents/models.py:436
msgid "created month is"
msgstr ""
#: documents/models.py:437
msgid "created day is"
msgstr ""
#: documents/models.py:438
msgid "added before"
msgstr ""
#: documents/models.py:439
msgid "added after"
msgstr ""
#: documents/models.py:440
msgid "modified before"
msgstr ""
#: documents/models.py:441
msgid "modified after"
msgid "View page size"
msgstr ""
#: documents/models.py:442
msgid "does not have tag"
msgstr ""
#: documents/models.py:443
msgid "does not have ASN"
msgstr ""
#: documents/models.py:444
msgid "title or content contains"
msgstr ""
#: documents/models.py:445
msgid "fulltext query"
msgstr ""
#: documents/models.py:446
msgid "more like this"
msgstr ""
#: documents/models.py:447
msgid "has tags in"
msgstr ""
#: documents/models.py:448
msgid "ASN greater than"
msgid "View display mode"
msgstr ""
#: documents/models.py:449
msgid "ASN less than"
msgid "Document display fields"
msgstr ""
#: documents/models.py:450
msgid "storage path is"
msgstr ""
#: documents/models.py:451
msgid "has correspondent in"
msgstr ""
#: documents/models.py:452
msgid "does not have correspondent in"
msgstr ""
#: documents/models.py:453
msgid "has document type in"
msgstr ""
#: documents/models.py:454
msgid "does not have document type in"
msgstr ""
#: documents/models.py:455
msgid "has storage path in"
msgstr ""
#: documents/models.py:456
msgid "does not have storage path in"
#: documents/models.py:456 documents/models.py:509
msgid "saved view"
msgstr ""
#: documents/models.py:457
msgid "owner is"
msgid "saved views"
msgstr ""
#: documents/models.py:458
msgid "has owner in"
#: documents/models.py:465
msgid "title contains"
msgstr ""
#: documents/models.py:459
msgid "does not have owner"
#: documents/models.py:466
msgid "content contains"
msgstr ""
#: documents/models.py:460
msgid "does not have owner in"
#: documents/models.py:467
msgid "ASN is"
msgstr ""
#: documents/models.py:461
msgid "has custom field value"
#: documents/models.py:468
msgid "correspondent is"
msgstr ""
#: documents/models.py:462
msgid "is shared by me"
#: documents/models.py:469
msgid "document type is"
msgstr ""
#: documents/models.py:470
msgid "is in inbox"
msgstr ""
#: documents/models.py:471
msgid "has tag"
msgstr ""
#: documents/models.py:472
msgid "rule type"
msgid "has any tag"
msgstr ""
#: documents/models.py:473
msgid "created before"
msgstr ""
#: documents/models.py:474
msgid "value"
msgid "created after"
msgstr ""
#: documents/models.py:475
msgid "created year is"
msgstr ""
#: documents/models.py:476
msgid "created month is"
msgstr ""
#: documents/models.py:477
msgid "filter rule"
msgid "created day is"
msgstr ""
#: documents/models.py:478
msgid "added before"
msgstr ""
#: documents/models.py:479
msgid "added after"
msgstr ""
#: documents/models.py:480
msgid "modified before"
msgstr ""
#: documents/models.py:481
msgid "modified after"
msgstr ""
#: documents/models.py:482
msgid "does not have tag"
msgstr ""
#: documents/models.py:483
msgid "does not have ASN"
msgstr ""
#: documents/models.py:484
msgid "title or content contains"
msgstr ""
#: documents/models.py:485
msgid "fulltext query"
msgstr ""
#: documents/models.py:486
msgid "more like this"
msgstr ""
#: documents/models.py:487
msgid "has tags in"
msgstr ""
#: documents/models.py:488
msgid "ASN greater than"
msgstr ""
#: documents/models.py:489
msgid "ASN less than"
msgstr ""
#: documents/models.py:490
msgid "storage path is"
msgstr ""
#: documents/models.py:491
msgid "has correspondent in"
msgstr ""
#: documents/models.py:492
msgid "does not have correspondent in"
msgstr ""
#: documents/models.py:493
msgid "has document type in"
msgstr ""
#: documents/models.py:494
msgid "does not have document type in"
msgstr ""
#: documents/models.py:495
msgid "has storage path in"
msgstr ""
#: documents/models.py:496
msgid "does not have storage path in"
msgstr ""
#: documents/models.py:497
msgid "owner is"
msgstr ""
#: documents/models.py:498
msgid "has owner in"
msgstr ""
#: documents/models.py:499
msgid "does not have owner"
msgstr ""
#: documents/models.py:500
msgid "does not have owner in"
msgstr ""
#: documents/models.py:501
msgid "has custom field value"
msgstr ""
#: documents/models.py:502
msgid "is shared by me"
msgstr ""
#: documents/models.py:512
msgid "rule type"
msgstr ""
#: documents/models.py:514
msgid "value"
msgstr ""
#: documents/models.py:517
msgid "filter rule"
msgstr ""
#: documents/models.py:518
msgid "filter rules"
msgstr ""
#: documents/models.py:589
#: documents/models.py:629
msgid "Task ID"
msgstr ""
#: documents/models.py:590
#: documents/models.py:630
msgid "Celery ID for the Task that was run"
msgstr ""
#: documents/models.py:595
#: documents/models.py:635
msgid "Acknowledged"
msgstr ""
#: documents/models.py:596
#: documents/models.py:636
msgid "If the task is acknowledged via the frontend or API"
msgstr ""
#: documents/models.py:602
#: documents/models.py:642
msgid "Task Filename"
msgstr ""
#: documents/models.py:603
#: documents/models.py:643
msgid "Name of the file which the Task was run for"
msgstr ""
#: documents/models.py:609
#: documents/models.py:649
msgid "Task Name"
msgstr ""
#: documents/models.py:610
#: documents/models.py:650
msgid "Name of the Task which was run"
msgstr ""
#: documents/models.py:617
#: documents/models.py:657
msgid "Task State"
msgstr ""
#: documents/models.py:618
#: documents/models.py:658
msgid "Current state of the task being run"
msgstr ""
#: documents/models.py:623
#: documents/models.py:663
msgid "Created DateTime"
msgstr ""
#: documents/models.py:624
#: documents/models.py:664
msgid "Datetime field when the task result was created in UTC"
msgstr ""
#: documents/models.py:629
#: documents/models.py:669
msgid "Started DateTime"
msgstr ""
#: documents/models.py:630
#: documents/models.py:670
msgid "Datetime field when the task was started in UTC"
msgstr ""
#: documents/models.py:635
#: documents/models.py:675
msgid "Completed DateTime"
msgstr ""
#: documents/models.py:636
#: documents/models.py:676
msgid "Datetime field when the task was completed in UTC"
msgstr ""
#: documents/models.py:641
#: documents/models.py:681
msgid "Result Data"
msgstr ""
#: documents/models.py:643
#: documents/models.py:683
msgid "The data returned by the task"
msgstr ""
#: documents/models.py:655
#: documents/models.py:695
msgid "Note for the document"
msgstr ""
#: documents/models.py:679
#: documents/models.py:719
msgid "user"
msgstr ""
#: documents/models.py:684
#: documents/models.py:724
msgid "note"
msgstr ""
#: documents/models.py:685
#: documents/models.py:725
msgid "notes"
msgstr ""
#: documents/models.py:693
#: documents/models.py:733
msgid "Archive"
msgstr ""
#: documents/models.py:694
#: documents/models.py:734
msgid "Original"
msgstr ""
#: documents/models.py:705
#: documents/models.py:745
msgid "expiration"
msgstr ""
#: documents/models.py:712
#: documents/models.py:752
msgid "slug"
msgstr ""
#: documents/models.py:744
#: documents/models.py:784
msgid "share link"
msgstr ""
#: documents/models.py:745
#: documents/models.py:785
msgid "share links"
msgstr ""
#: documents/models.py:757
#: documents/models.py:797
msgid "String"
msgstr ""
#: documents/models.py:758
#: documents/models.py:798
msgid "URL"
msgstr ""
#: documents/models.py:759
#: documents/models.py:799
msgid "Date"
msgstr ""
#: documents/models.py:760
#: documents/models.py:800
msgid "Boolean"
msgstr ""
#: documents/models.py:761
#: documents/models.py:801
msgid "Integer"
msgstr ""
#: documents/models.py:762
#: documents/models.py:802
msgid "Float"
msgstr ""
#: documents/models.py:763
#: documents/models.py:803
msgid "Monetary"
msgstr ""
#: documents/models.py:764
#: documents/models.py:804
msgid "Document Link"
msgstr ""
#: documents/models.py:776
#: documents/models.py:816
msgid "data type"
msgstr ""
#: documents/models.py:784
#: documents/models.py:824
msgid "custom field"
msgstr ""
#: documents/models.py:785
#: documents/models.py:825
msgid "custom fields"
msgstr ""
#: documents/models.py:847
#: documents/models.py:887
msgid "custom field instance"
msgstr ""
#: documents/models.py:848
#: documents/models.py:888
msgid "custom field instances"
msgstr ""
#: documents/models.py:910
#: documents/models.py:950
msgid "Consumption Started"
msgstr ""
#: documents/models.py:911
#: documents/models.py:951
msgid "Document Added"
msgstr ""
#: documents/models.py:912
#: documents/models.py:952
msgid "Document Updated"
msgstr ""
#: documents/models.py:915
#: documents/models.py:955
msgid "Consume Folder"
msgstr ""
#: documents/models.py:916
#: documents/models.py:956
msgid "Api Upload"
msgstr ""
#: documents/models.py:917
#: documents/models.py:957
msgid "Mail Fetch"
msgstr ""
#: documents/models.py:920
#: documents/models.py:960
msgid "Workflow Trigger Type"
msgstr ""
#: documents/models.py:932
#: documents/models.py:972
msgid "filter path"
msgstr ""
#: documents/models.py:937
#: documents/models.py:977
msgid ""
"Only consume documents with a path that matches this if specified. Wildcards "
"specified as * are allowed. Case insensitive."
msgstr ""
#: documents/models.py:944
#: documents/models.py:984
msgid "filter filename"
msgstr ""
#: documents/models.py:949 paperless_mail/models.py:148
#: documents/models.py:989 paperless_mail/models.py:148
msgid ""
"Only consume documents which entirely match this filename if specified. "
"Wildcards such as *.pdf or *invoice* are allowed. Case insensitive."
msgstr ""
#: documents/models.py:960
#: documents/models.py:1000
msgid "filter documents from this mail rule"
msgstr ""
#: documents/models.py:976
#: documents/models.py:1016
msgid "has these tag(s)"
msgstr ""
#: documents/models.py:984
#: documents/models.py:1024
msgid "has this document type"
msgstr ""
#: documents/models.py:992
#: documents/models.py:1032
msgid "has this correspondent"
msgstr ""
#: documents/models.py:996
#: documents/models.py:1036
msgid "workflow trigger"
msgstr ""
#: documents/models.py:997
#: documents/models.py:1037
msgid "workflow triggers"
msgstr ""
#: documents/models.py:1007
#: documents/models.py:1047
msgid "Assignment"
msgstr ""
#: documents/models.py:1011
#: documents/models.py:1051
msgid "Removal"
msgstr ""
#: documents/models.py:1015
#: documents/models.py:1055
msgid "Workflow Action Type"
msgstr ""
#: documents/models.py:1021
#: documents/models.py:1061
msgid "assign title"
msgstr ""
#: documents/models.py:1026
#: documents/models.py:1066
msgid ""
"Assign a document title, can include some placeholders, see documentation."
msgstr ""
#: documents/models.py:1035 paperless_mail/models.py:216
#: documents/models.py:1075 paperless_mail/models.py:216
msgid "assign this tag"
msgstr ""
#: documents/models.py:1044 paperless_mail/models.py:224
#: documents/models.py:1084 paperless_mail/models.py:224
msgid "assign this document type"
msgstr ""
#: documents/models.py:1053 paperless_mail/models.py:238
#: documents/models.py:1093 paperless_mail/models.py:238
msgid "assign this correspondent"
msgstr ""
#: documents/models.py:1062
#: documents/models.py:1102
msgid "assign this storage path"
msgstr ""
#: documents/models.py:1071
#: documents/models.py:1111
msgid "assign this owner"
msgstr ""
#: documents/models.py:1078
#: documents/models.py:1118
msgid "grant view permissions to these users"
msgstr ""
#: documents/models.py:1085
#: documents/models.py:1125
msgid "grant view permissions to these groups"
msgstr ""
#: documents/models.py:1092
#: documents/models.py:1132
msgid "grant change permissions to these users"
msgstr ""
#: documents/models.py:1099
#: documents/models.py:1139
msgid "grant change permissions to these groups"
msgstr ""
#: documents/models.py:1106
#: documents/models.py:1146
msgid "assign these custom fields"
msgstr ""
#: documents/models.py:1113
#: documents/models.py:1153
msgid "remove these tag(s)"
msgstr ""
#: documents/models.py:1118
#: documents/models.py:1158
msgid "remove all tags"
msgstr ""
#: documents/models.py:1125
#: documents/models.py:1165
msgid "remove these document type(s)"
msgstr ""
#: documents/models.py:1130
#: documents/models.py:1170
msgid "remove all document types"
msgstr ""
#: documents/models.py:1137
#: documents/models.py:1177
msgid "remove these correspondent(s)"
msgstr ""
#: documents/models.py:1142
#: documents/models.py:1182
msgid "remove all correspondents"
msgstr ""
#: documents/models.py:1149
#: documents/models.py:1189
msgid "remove these storage path(s)"
msgstr ""
#: documents/models.py:1154
#: documents/models.py:1194
msgid "remove all storage paths"
msgstr ""
#: documents/models.py:1161
#: documents/models.py:1201
msgid "remove these owner(s)"
msgstr ""
#: documents/models.py:1166
#: documents/models.py:1206
msgid "remove all owners"
msgstr ""
#: documents/models.py:1173
#: documents/models.py:1213
msgid "remove view permissions for these users"
msgstr ""
#: documents/models.py:1180
#: documents/models.py:1220
msgid "remove view permissions for these groups"
msgstr ""
#: documents/models.py:1187
#: documents/models.py:1227
msgid "remove change permissions for these users"
msgstr ""
#: documents/models.py:1194
#: documents/models.py:1234
msgid "remove change permissions for these groups"
msgstr ""
#: documents/models.py:1199
#: documents/models.py:1239
msgid "remove all permissions"
msgstr ""
#: documents/models.py:1206
#: documents/models.py:1246
msgid "remove these custom fields"
msgstr ""
#: documents/models.py:1211
#: documents/models.py:1251
msgid "remove all custom fields"
msgstr ""
#: documents/models.py:1215
#: documents/models.py:1255
msgid "workflow action"
msgstr ""
#: documents/models.py:1216
#: documents/models.py:1256
msgid "workflow actions"
msgstr ""
#: documents/models.py:1225 paperless_mail/models.py:95
#: documents/models.py:1265 paperless_mail/models.py:95
msgid "order"
msgstr ""
#: documents/models.py:1231
#: documents/models.py:1271
msgid "triggers"
msgstr ""
#: documents/models.py:1238
#: documents/models.py:1278
msgid "actions"
msgstr ""
#: documents/models.py:1241
#: documents/models.py:1281
msgid "enabled"
msgstr ""
@@ -849,12 +917,12 @@ msgstr ""
msgid "Invalid color."
msgstr ""
#: documents/serialisers.py:1148
#: documents/serialisers.py:1169
#, python-format
msgid "File type %(type)s not supported"
msgstr ""
#: documents/serialisers.py:1257
#: documents/serialisers.py:1278
msgid "Invalid variable detected."
msgstr ""