This commit is contained in:
shamoon 2024-04-13 23:59:39 -07:00
parent aeaa0d18e2
commit e25f361907
13 changed files with 34 additions and 33 deletions

View File

@ -140,7 +140,7 @@ document. Paperless only reports PDF metadata at this point.
- `/api/documents/<id>/notes/`: Retrieve notes for a document. - `/api/documents/<id>/notes/`: Retrieve notes for a document.
- `/api/documents/<id>/share_links/`: Retrieve share links for a document. - `/api/documents/<id>/share_links/`: Retrieve share links for a document.
- `/api/documents/<id>/audit/`: Retrieve history of changes for a document. - `/api/documents/<id>/history/`: Retrieve history of changes for a document.
## Authorization ## Authorization

View File

@ -119,7 +119,7 @@ import { NgxFilesizeModule } from 'ngx-filesize'
import { RotateConfirmDialogComponent } from './components/common/confirm-dialog/rotate-confirm-dialog/rotate-confirm-dialog.component' import { RotateConfirmDialogComponent } from './components/common/confirm-dialog/rotate-confirm-dialog/rotate-confirm-dialog.component'
import { MergeConfirmDialogComponent } from './components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component' import { MergeConfirmDialogComponent } from './components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component'
import { SplitConfirmDialogComponent } from './components/common/confirm-dialog/split-confirm-dialog/split-confirm-dialog.component' import { SplitConfirmDialogComponent } from './components/common/confirm-dialog/split-confirm-dialog/split-confirm-dialog.component'
import { AuditLogComponent } from './components/audit-log/audit-log.component' import { DocumentHistoryComponent } from './components/document-history/document-history.component'
import { import {
airplane, airplane,
archive, archive,
@ -473,7 +473,7 @@ function initializeApp(settings: SettingsService) {
RotateConfirmDialogComponent, RotateConfirmDialogComponent,
MergeConfirmDialogComponent, MergeConfirmDialogComponent,
SplitConfirmDialogComponent, SplitConfirmDialogComponent,
AuditLogComponent, DocumentHistoryComponent,
], ],
imports: [ imports: [
BrowserModule, BrowserModule,

View File

@ -285,12 +285,12 @@
</li> </li>
} }
@if (auditlogEnabled) { @if (historyEnabled) {
<li [ngbNavItem]="DocumentDetailNavIDs.History"> <li [ngbNavItem]="DocumentDetailNavIDs.History">
<a ngbNavLink i18n>History</a> <a ngbNavLink i18n>History</a>
<ng-template ngbNavContent> <ng-template ngbNavContent>
<div class="mb-3"> <div class="mb-3">
<pngx-audit-log [documentId]="documentId"></pngx-audit-log> <pngx-document-history [documentId]="documentId"></pngx-document-history>
</div> </div>
</ng-template> </ng-template>
</li> </li>

View File

@ -903,12 +903,13 @@ export class DocumentDetailComponent
) )
} }
get auditlogEnabled(): boolean { get historyEnabled(): boolean {
return ( return (
this.settings.get(SETTINGS_KEYS.AUDITLOG_ENABLED) && this.settings.get(SETTINGS_KEYS.AUDITLOG_ENABLED) &&
this.userIsOwner &&
this.permissionsService.currentUserCan( this.permissionsService.currentUserCan(
PermissionAction.View, PermissionAction.View,
PermissionType.AuditLogEntry PermissionType.History
) )
) )
} }

View File

@ -1,6 +1,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing' import { ComponentFixture, TestBed } from '@angular/core/testing'
import { AuditLogComponent } from './audit-log.component' import { DocumentHistoryComponent } from './document-history.component'
import { DocumentService } from 'src/app/services/rest/document.service' import { DocumentService } from 'src/app/services/rest/document.service'
import { of } from 'rxjs' import { of } from 'rxjs'
import { AuditLogAction } from 'src/app/data/auditlog-entry' import { AuditLogAction } from 'src/app/data/auditlog-entry'
@ -10,14 +10,14 @@ import { DatePipe } from '@angular/common'
import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap' import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
describe('AuditLogComponent', () => { describe('DocumentHistoryComponent', () => {
let component: AuditLogComponent let component: DocumentHistoryComponent
let fixture: ComponentFixture<AuditLogComponent> let fixture: ComponentFixture<DocumentHistoryComponent>
let documentService: DocumentService let documentService: DocumentService
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [AuditLogComponent, CustomDatePipe], declarations: [DocumentHistoryComponent, CustomDatePipe],
providers: [DatePipe], providers: [DatePipe],
imports: [ imports: [
HttpClientTestingModule, HttpClientTestingModule,
@ -26,14 +26,14 @@ describe('AuditLogComponent', () => {
], ],
}).compileComponents() }).compileComponents()
fixture = TestBed.createComponent(AuditLogComponent) fixture = TestBed.createComponent(DocumentHistoryComponent)
documentService = TestBed.inject(DocumentService) documentService = TestBed.inject(DocumentService)
component = fixture.componentInstance component = fixture.componentInstance
}) })
it('should get audit log entries on init', () => { it('should get audit log entries on init', () => {
const getAuditLogSpy = jest.spyOn(documentService, 'getAuditLog') const getHistorySpy = jest.spyOn(documentService, 'getHistory')
getAuditLogSpy.mockReturnValue( getHistorySpy.mockReturnValue(
of([ of([
{ {
id: 1, id: 1,
@ -52,6 +52,6 @@ describe('AuditLogComponent', () => {
) )
component.documentId = 1 component.documentId = 1
fixture.detectChanges() fixture.detectChanges()
expect(getAuditLogSpy).toHaveBeenCalledWith(1) expect(getHistorySpy).toHaveBeenCalledWith(1)
}) })
}) })

View File

@ -3,11 +3,11 @@ import { AuditLogAction, AuditLogEntry } from 'src/app/data/auditlog-entry'
import { DocumentService } from 'src/app/services/rest/document.service' import { DocumentService } from 'src/app/services/rest/document.service'
@Component({ @Component({
selector: 'pngx-audit-log', selector: 'pngx-document-history',
templateUrl: './audit-log.component.html', templateUrl: './document-history.component.html',
styleUrl: './audit-log.component.scss', styleUrl: './document-history.component.scss',
}) })
export class AuditLogComponent implements OnInit { export class DocumentHistoryComponent implements OnInit {
public AuditLogAction = AuditLogAction public AuditLogAction = AuditLogAction
private _documentId: number private _documentId: number
@ -26,7 +26,7 @@ export class AuditLogComponent implements OnInit {
if (this._documentId) { if (this._documentId) {
this.loading = true this.loading = true
this.documentService this.documentService
.getAuditLog(this._documentId) .getHistory(this._documentId)
.subscribe((auditLogEntries) => { .subscribe((auditLogEntries) => {
this.entries = auditLogEntries this.entries = auditLogEntries
this.loading = false this.loading = false

View File

@ -19,7 +19,7 @@ export enum PermissionType {
PaperlessTask = '%s_paperlesstask', PaperlessTask = '%s_paperlesstask',
AppConfig = '%s_applicationconfiguration', AppConfig = '%s_applicationconfiguration',
UISettings = '%s_uisettings', UISettings = '%s_uisettings',
AuditLogEntry = '%s_logentry', History = '%s_logentry',
Note = '%s_note', Note = '%s_note',
MailAccount = '%s_mailaccount', MailAccount = '%s_mailaccount',
MailRule = '%s_mailrule', MailRule = '%s_mailrule',

View File

@ -268,9 +268,9 @@ describe(`DocumentService`, () => {
}) })
it('should call appropriate api endpoint for getting audit log', () => { it('should call appropriate api endpoint for getting audit log', () => {
subscription = service.getAuditLog(documents[0].id).subscribe() subscription = service.getHistory(documents[0].id).subscribe()
const req = httpTestingController.expectOne( const req = httpTestingController.expectOne(
`${environment.apiBaseUrl}${endpoint}/${documents[0].id}/audit/` `${environment.apiBaseUrl}${endpoint}/${documents[0].id}/history/`
) )
}) })
}) })

View File

@ -223,8 +223,8 @@ export class DocumentService extends AbstractPaperlessService<Document> {
) )
} }
getAuditLog(id: number): Observable<AuditLogEntry[]> { getHistory(id: number): Observable<AuditLogEntry[]> {
return this.http.get<AuditLogEntry[]>(this.getResourceUrl(id, 'audit')) return this.http.get<AuditLogEntry[]>(this.getResourceUrl(id, 'history'))
} }
bulkDownload( bulkDownload(

View File

@ -316,7 +316,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
response = self.client.get(f"/api/documents/{doc.pk}/thumb/") response = self.client.get(f"/api/documents/{doc.pk}/thumb/")
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
def test_document_audit_action(self): def test_document_history_action(self):
""" """
GIVEN: GIVEN:
- Document - Document
@ -337,7 +337,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
format="json", format="json",
) )
response = self.client.get(f"/api/documents/{doc.pk}/audit/") 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(len(response.data), 2) self.assertEqual(len(response.data), 2)
self.assertEqual(response.data[0]["actor"]["id"], self.user.id) self.assertEqual(response.data[0]["actor"]["id"], self.user.id)
@ -347,7 +347,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
{"title": ["First title", "New title"]}, {"title": ["First title", "New title"]},
) )
def test_document_audit_action_w_custom_fields(self): def test_document_history_action_w_custom_fields(self):
""" """
GIVEN: GIVEN:
- Document with custom fields - Document with custom fields
@ -379,7 +379,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
format="json", format="json",
) )
response = self.client.get(f"/api/documents/{doc.pk}/audit/") 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(response.data[1]["actor"]["id"], self.user.id)
self.assertEqual(response.data[1]["action"], "create") self.assertEqual(response.data[1]["action"], "create")
@ -395,7 +395,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
) )
@override_settings(AUDIT_LOG_ENABLED=False) @override_settings(AUDIT_LOG_ENABLED=False)
def test_document_audit_action_disabled(self): def test_document_history_action_disabled(self):
""" """
GIVEN: GIVEN:
- Audit log is disabled - Audit log is disabled
@ -417,7 +417,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
format="json", format="json",
) )
response = self.client.get(f"/api/documents/{doc.pk}/audit/") response = self.client.get(f"/api/documents/{doc.pk}/history/")
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_document_filters(self): def test_document_filters(self):

View File

@ -732,7 +732,7 @@ class DocumentViewSet(
return Response(links) return Response(links)
@action(methods=["get"], detail=True, name="Audit Trail") @action(methods=["get"], detail=True, name="Audit Trail")
def audit(self, request, pk=None): def history(self, request, pk=None):
if not settings.AUDIT_LOG_ENABLED: if not settings.AUDIT_LOG_ENABLED:
return HttpResponseBadRequest("Audit log is disabled") return HttpResponseBadRequest("Audit log is disabled")
try: try: