mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-04-19 10:19:27 -05:00
Fix: Document history could include extra fields (#6989)
* Fixes creation of a custom field being included in a document's history even if not attached * Show custom field creation in UI --------- Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
This commit is contained in:
parent
fa7a5451db
commit
61485b0f1d
@ -27,31 +27,33 @@
|
|||||||
}
|
}
|
||||||
<span class="badge bg-secondary ms-auto" [class.bg-primary]="entry.action === AuditLogAction.Create">{{ entry.action | titlecase }}</span>
|
<span class="badge bg-secondary ms-auto" [class.bg-primary]="entry.action === AuditLogAction.Create">{{ entry.action | titlecase }}</span>
|
||||||
</div>
|
</div>
|
||||||
@if (entry.action === AuditLogAction.Update) {
|
<ul class="mt-2">
|
||||||
<ul class="mt-2">
|
@for (change of entry.changes | keyvalue; track change.key) {
|
||||||
@for (change of entry.changes | keyvalue; track change.key) {
|
@if (change.value["type"] === 'm2m') {
|
||||||
@if (change.value["type"] === 'm2m') {
|
<li>
|
||||||
<li>
|
<span class="fst-italic">{{ change.value["operation"] | titlecase }}</span>
|
||||||
<span class="fst-italic">{{ change.value["operation"] | titlecase }}</span>
|
<span>{{ change.key | titlecase }}</span>:
|
||||||
<span>{{ change.key | titlecase }}</span>:
|
<code class="text-primary">{{ change.value["objects"].join(', ') }}</code>
|
||||||
<code class="text-primary">{{ change.value["objects"].join(', ') }}</code>
|
</li>
|
||||||
</li>
|
|
||||||
}
|
|
||||||
@else if (change.value["type"] === 'custom_field') {
|
|
||||||
<li>
|
|
||||||
<span>{{ change.value["field"] }}</span>:
|
|
||||||
<code class="text-primary">{{ change.value["value"] }}</code>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
@else {
|
|
||||||
<li>
|
|
||||||
<span>{{ change.key | titlecase }}</span>:
|
|
||||||
<code class="text-primary">{{ change.value[1] }}</code>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</ul>
|
@else if (change.value["type"] === 'custom_field') {
|
||||||
}
|
<li>
|
||||||
|
<span>{{ change.value["field"] }}</span>:
|
||||||
|
<code class="text-primary">{{ change.value["value"] }}</code>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
@else {
|
||||||
|
<li>
|
||||||
|
<span>{{ change.key | titlecase }}</span>:
|
||||||
|
@if (change.key === 'content') {
|
||||||
|
<code class="text-primary">{{ change.value[1]?.substring(0,100) }}...</code>
|
||||||
|
} @else {
|
||||||
|
<code class="text-primary">{{ change.value[1] }}</code>
|
||||||
|
}
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -366,6 +366,16 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
|
|||||||
data_type=CustomField.FieldDataType.STRING,
|
data_type=CustomField.FieldDataType.STRING,
|
||||||
)
|
)
|
||||||
self.client.force_login(user=self.user)
|
self.client.force_login(user=self.user)
|
||||||
|
|
||||||
|
# Initial response should include only document's creation
|
||||||
|
response = self.client.get(f"/api/documents/{doc.pk}/history/")
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(len(response.data), 1)
|
||||||
|
|
||||||
|
self.assertIsNone(response.data[0]["actor"])
|
||||||
|
self.assertEqual(response.data[0]["action"], "create")
|
||||||
|
|
||||||
self.client.patch(
|
self.client.patch(
|
||||||
f"/api/documents/{doc.pk}/",
|
f"/api/documents/{doc.pk}/",
|
||||||
data={
|
data={
|
||||||
@ -379,12 +389,15 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
|
|||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Second response should include custom field addition
|
||||||
response = self.client.get(f"/api/documents/{doc.pk}/history/")
|
response = self.client.get(f"/api/documents/{doc.pk}/history/")
|
||||||
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
self.assertEqual(response.data[1]["actor"]["id"], self.user.id)
|
self.assertEqual(len(response.data), 2)
|
||||||
self.assertEqual(response.data[1]["action"], "create")
|
self.assertEqual(response.data[0]["actor"]["id"], self.user.id)
|
||||||
|
self.assertEqual(response.data[0]["action"], "create")
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
response.data[1]["changes"],
|
response.data[0]["changes"],
|
||||||
{
|
{
|
||||||
"custom_fields": {
|
"custom_fields": {
|
||||||
"type": "custom_field",
|
"type": "custom_field",
|
||||||
@ -393,6 +406,8 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
self.assertIsNone(response.data[1]["actor"])
|
||||||
|
self.assertEqual(response.data[1]["action"], "create")
|
||||||
|
|
||||||
@override_settings(AUDIT_LOG_ENABLED=False)
|
@override_settings(AUDIT_LOG_ENABLED=False)
|
||||||
def test_document_history_action_disabled(self):
|
def test_document_history_action_disabled(self):
|
||||||
|
@ -19,7 +19,6 @@ from django.apps import apps
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.contenttypes.models import ContentType
|
|
||||||
from django.db import connections
|
from django.db import connections
|
||||||
from django.db.migrations.loader import MigrationLoader
|
from django.db.migrations.loader import MigrationLoader
|
||||||
from django.db.migrations.recorder import MigrationRecorder
|
from django.db.migrations.recorder import MigrationRecorder
|
||||||
@ -106,7 +105,6 @@ from documents.matching import match_storage_paths
|
|||||||
from documents.matching import match_tags
|
from documents.matching import match_tags
|
||||||
from documents.models import Correspondent
|
from documents.models import Correspondent
|
||||||
from documents.models import CustomField
|
from documents.models import CustomField
|
||||||
from documents.models import CustomFieldInstance
|
|
||||||
from documents.models import Document
|
from documents.models import Document
|
||||||
from documents.models import DocumentType
|
from documents.models import DocumentType
|
||||||
from documents.models import Note
|
from documents.models import Note
|
||||||
@ -799,15 +797,14 @@ class DocumentViewSet(
|
|||||||
else None
|
else None
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
for entry in LogEntry.objects.filter(object_pk=doc.pk).select_related(
|
for entry in LogEntry.objects.get_for_object(doc).select_related(
|
||||||
"actor",
|
"actor",
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
# custom fields
|
# custom fields
|
||||||
for entry in LogEntry.objects.filter(
|
for entry in LogEntry.objects.get_for_objects(
|
||||||
object_pk__in=list(doc.custom_fields.values_list("id", flat=True)),
|
doc.custom_fields.all(),
|
||||||
content_type=ContentType.objects.get_for_model(CustomFieldInstance),
|
|
||||||
).select_related("actor"):
|
).select_related("actor"):
|
||||||
entries.append(
|
entries.append(
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user