mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-08-12 00:19:48 +00:00
Feature: document history (audit log UI) (#6388)
This commit is contained in:
@@ -18,6 +18,7 @@ import pathvalidate
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import connections
|
||||
from django.db.migrations.loader import MigrationLoader
|
||||
from django.db.migrations.recorder import MigrationRecorder
|
||||
@@ -105,6 +106,7 @@ from documents.matching import match_storage_paths
|
||||
from documents.matching import match_tags
|
||||
from documents.models import Correspondent
|
||||
from documents.models import CustomField
|
||||
from documents.models import CustomFieldInstance
|
||||
from documents.models import Document
|
||||
from documents.models import DocumentType
|
||||
from documents.models import Note
|
||||
@@ -729,6 +731,66 @@ class DocumentViewSet(
|
||||
]
|
||||
return Response(links)
|
||||
|
||||
@action(methods=["get"], detail=True, name="Audit Trail")
|
||||
def history(self, request, pk=None):
|
||||
if not settings.AUDIT_LOG_ENABLED:
|
||||
return HttpResponseBadRequest("Audit log is disabled")
|
||||
try:
|
||||
doc = Document.objects.get(pk=pk)
|
||||
if not request.user.has_perm("auditlog.view_logentry") or (
|
||||
doc.owner is not None and doc.owner != request.user
|
||||
):
|
||||
return HttpResponseForbidden(
|
||||
"Insufficient permissions",
|
||||
)
|
||||
except Document.DoesNotExist: # pragma: no cover
|
||||
raise Http404
|
||||
|
||||
# documents
|
||||
entries = [
|
||||
{
|
||||
"id": entry.id,
|
||||
"timestamp": entry.timestamp,
|
||||
"action": entry.get_action_display(),
|
||||
"changes": entry.changes,
|
||||
"actor": (
|
||||
{"id": entry.actor.id, "username": entry.actor.username}
|
||||
if entry.actor
|
||||
else None
|
||||
),
|
||||
}
|
||||
for entry in LogEntry.objects.filter(object_pk=doc.pk).select_related(
|
||||
"actor",
|
||||
)
|
||||
]
|
||||
|
||||
# custom fields
|
||||
for entry in LogEntry.objects.filter(
|
||||
object_pk__in=doc.custom_fields.values_list("id", flat=True),
|
||||
content_type=ContentType.objects.get_for_model(CustomFieldInstance),
|
||||
).select_related("actor"):
|
||||
entries.append(
|
||||
{
|
||||
"id": entry.id,
|
||||
"timestamp": entry.timestamp,
|
||||
"action": entry.get_action_display(),
|
||||
"changes": {
|
||||
"custom_fields": {
|
||||
"type": "custom_field",
|
||||
"field": str(entry.object_repr).split(":")[0].strip(),
|
||||
"value": str(entry.object_repr).split(":")[1].strip(),
|
||||
},
|
||||
},
|
||||
"actor": (
|
||||
{"id": entry.actor.id, "username": entry.actor.username}
|
||||
if entry.actor
|
||||
else None
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
return Response(sorted(entries, key=lambda x: x["timestamp"], reverse=True))
|
||||
|
||||
|
||||
class SearchResultSerializer(DocumentSerializer, PassUserMixin):
|
||||
def to_representation(self, instance):
|
||||
@@ -1267,6 +1329,8 @@ class UiSettingsView(GenericAPIView):
|
||||
if general_config.app_logo is not None and len(general_config.app_logo) > 0:
|
||||
ui_settings["app_logo"] = general_config.app_logo
|
||||
|
||||
ui_settings["auditlog_enabled"] = settings.AUDIT_LOG_ENABLED
|
||||
|
||||
user_resp = {
|
||||
"id": user.id,
|
||||
"username": user.username,
|
||||
|
Reference in New Issue
Block a user