mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-11-01 20:45:35 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30f83026ab | ||
|
|
5b63c9a0ff | ||
|
|
217c5e681d |
No files matched your search
+1
-1
@@ -38,7 +38,7 @@
|
||||
"ngx-device-detector": "^12.0.0",
|
||||
"ngx-ui-tour-ng-bootstrap": "^19.0.0",
|
||||
"normalize-diacritics": "^5.0.0",
|
||||
"pdfjs-dist": "^6.3.289",
|
||||
"pdfjs-dist": "^6.4.299",
|
||||
"rxjs": "^7.8.2",
|
||||
"tslib": "^2.8.1",
|
||||
"utif": "^3.1.0",
|
||||
|
||||
Generated
+53
-53
@@ -82,8 +82,8 @@ importers:
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0
|
||||
pdfjs-dist:
|
||||
specifier: ^6.3.289
|
||||
version: 6.3.289
|
||||
specifier: ^6.4.299
|
||||
version: 6.4.299
|
||||
rxjs:
|
||||
specifier: ^7.8.2
|
||||
version: 7.8.2
|
||||
@@ -1303,79 +1303,79 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@napi-rs/canvas-android-arm64@1.0.9':
|
||||
resolution: {integrity: sha512-4LGXk2/0HVzE29K8SzML5WubgCp++B1FH3qgl35XmSZE+lLdr6P9VRQEnZ0MCLZMTSuJP41yyhtoiVNEuvrTIA==}
|
||||
'@napi-rs/canvas-android-arm64@1.0.13':
|
||||
resolution: {integrity: sha512-trn7AaDzQ+yeeA0ezhkC7cB1ZDPtp4UYk0EvB5F8qtYL9jEVWtVrU30WOpI52DszoTxaTsCgf5PP4ZVaOlOIHQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@napi-rs/canvas-darwin-arm64@1.0.9':
|
||||
resolution: {integrity: sha512-YNdfLBzY0W/Pep9fo2L6RmoNlNksnn05LRnX66W63R3ij58S25QOTcjdtEt2v8+PnCESzqZsYzUo+QPeIR44NA==}
|
||||
'@napi-rs/canvas-darwin-arm64@1.0.13':
|
||||
resolution: {integrity: sha512-6Y2djEetL8copxfNK6hg7oig/xqTwOchv19hBOm+zBNdKc/02Z3kjZN5wrLzv/l5O04/bAQS6I/AlQpwBlXgPg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@napi-rs/canvas-darwin-x64@1.0.9':
|
||||
resolution: {integrity: sha512-ceZQSknTEcy3dOXoekv59LTCkXjvnLsq+VW5PeNNDHEPQbRS5Ervkm1EaDa7WLAjiYWMLuSQTRTHao1dEX4prg==}
|
||||
'@napi-rs/canvas-darwin-x64@1.0.13':
|
||||
resolution: {integrity: sha512-OZoPwwu4TMMg8GTCFBrg8UFExwQTU/55i3uTC/izDat6PHE+ro9dimyQoOboFf91+NY7Pnn70ScR8f8I8+eZXw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@napi-rs/canvas-linux-arm-gnueabihf@1.0.9':
|
||||
resolution: {integrity: sha512-XhfI0Wwv4llhd6nnWDtY3kQKjq0r+y1i91PlJlJI24ag2U9WrnwbG1qS3+fDLEyouwEVFchiKkTEHozK+5iUNA==}
|
||||
'@napi-rs/canvas-linux-arm-gnueabihf@1.0.13':
|
||||
resolution: {integrity: sha512-dZSYaKROuswU2eUWG5aBKndNVe68elo1YX93xtRax7q658tj8rYE+HwduovWJgs6qwv1r1/CLR60nDeBbyjvyg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@napi-rs/canvas-linux-arm64-gnu@1.0.9':
|
||||
resolution: {integrity: sha512-012oiYtKaE7i9oxc8q7nraT7kDOpLcaCmFLzVe9Ty34RHDdoDzbWLrVh827CNxYh/EADX1eSikA3ymLjo/nNuw==}
|
||||
'@napi-rs/canvas-linux-arm64-gnu@1.0.13':
|
||||
resolution: {integrity: sha512-W6FzYE8ADHI93In1vi80KBL6btDtAMuj50Ch5ptkWS8xuntyzbR4h5JDUmXoakxZn2yMInksudTEqVSWoFqFGg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@napi-rs/canvas-linux-arm64-musl@1.0.9':
|
||||
resolution: {integrity: sha512-Ls5UWYFFn63casTZEczbeyEg3vRDRkv9lscuGwfchtY5yLLQhgOB8SN4YGxmfJ5vTaBwZ2YUxBS3NtmjJmFXdA==}
|
||||
'@napi-rs/canvas-linux-arm64-musl@1.0.13':
|
||||
resolution: {integrity: sha512-+6fQ+rR49hkAa4voh2stLIwQ+4t+gmoseM8Bc53/DlMXb73IKymDPWK9A/NtWeqTW4ijWppYuyuxS2hPI8tzNA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@napi-rs/canvas-linux-riscv64-gnu@1.0.9':
|
||||
resolution: {integrity: sha512-hLKEGxV7ZiRHqndePTokgDMdBlo/rDfzg7P4p4QIv9pUhuYobnu3R2NIFLCRghG0nwfo+s2sw+c1xZFeCmEAsw==}
|
||||
'@napi-rs/canvas-linux-riscv64-gnu@1.0.13':
|
||||
resolution: {integrity: sha512-17XEMO3t9RW/TtHtSFf2AkaSWuCWdzbPeidz7zrOFawHxoLSJCnpTI1hN5aUvrZNnkNNmt/vZVJCh0ryUaik3A==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@napi-rs/canvas-linux-x64-gnu@1.0.9':
|
||||
resolution: {integrity: sha512-6kaz3w0QMy77PDWk6rJ1ksIihdad3qzEyX2o2oGT8GwCaypfT5mhjr8buOO5hstyLxcWXDScuz56RsINLtBPIQ==}
|
||||
'@napi-rs/canvas-linux-x64-gnu@1.0.13':
|
||||
resolution: {integrity: sha512-lc48IwknVHBsNzXn9Xs5OkUsRX38Ml3Z1aOybQ51mMEzZckm8+4A0qROlyYHEoBPzr1kL3IU8ZUAqDb7wtdX8w==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@napi-rs/canvas-linux-x64-musl@1.0.9':
|
||||
resolution: {integrity: sha512-xrGvmS3v55hmZ86ls/kBLVNMUTYio3f6Ik0DireemG994VfPAwiA3ZXA0Uf1bByctkB3NQ1Sfb+H5bkdUnnzfQ==}
|
||||
'@napi-rs/canvas-linux-x64-musl@1.0.13':
|
||||
resolution: {integrity: sha512-bGY1r26S2bs8UNFJcPWcCSNdCFbKep5PATsvtgtC9A5u6SRgr2/jJE/RlhHL5m9ZpixNDhVkLY/5h16Kx5V9YA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@napi-rs/canvas-win32-arm64-msvc@1.0.9':
|
||||
resolution: {integrity: sha512-yjmVS3ArZeRVCP7jqbPq4rpZa/BhTeI7ELE2XqJg3snICQBDevLZyArxswHkiTnT34KRic33/4fLirrHI+SY8A==}
|
||||
'@napi-rs/canvas-win32-arm64-msvc@1.0.13':
|
||||
resolution: {integrity: sha512-2vRPBZoaIvJrUkDhZTE2Hn0YrnpJRCWVM3EJACoBKjuWI6WgPxGtUoRMsxRviCsyks8bKTSFdyw14CellnG2qA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@napi-rs/canvas-win32-x64-msvc@1.0.9':
|
||||
resolution: {integrity: sha512-QlSYQdMQslB81nlABo9wNfQ6npFhE7/O+saCZdqVGueGanyRk4jCogD5EwQenfP3kIq9e+mm6GreQBjX5MrA8g==}
|
||||
'@napi-rs/canvas-win32-x64-msvc@1.0.13':
|
||||
resolution: {integrity: sha512-DUlW3lZPz63kYsf8mxgyyZuevhlH0T3/BGP9l+NazhB0F1+gL6D4hqZdnhZpw24DlWmTpATDcyYSZlJOi+DdfQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@napi-rs/canvas@1.0.9':
|
||||
resolution: {integrity: sha512-QviPdJImDi/jMAvBfqaw+19BndMd/sizXVW3NnpMd3VJGz++QXkOHcP9kWR/smHG0hNjHeyuHFyrx/5lD0oNcQ==}
|
||||
'@napi-rs/canvas@1.0.13':
|
||||
resolution: {integrity: sha512-b87ugZpQ5ViwqVBkjervxpwa0GmJPsKHLBNoZN6GpjWeHzTbDUGryriq+2DYW2XbB+xCqSIINQysC0/I87nPbw==}
|
||||
engines: {node: '>= 10'}
|
||||
|
||||
'@napi-rs/nice-android-arm-eabi@1.1.1':
|
||||
@@ -3564,8 +3564,8 @@ packages:
|
||||
resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==}
|
||||
engines: {node: 18 || 20 || >=22}
|
||||
|
||||
pdfjs-dist@6.3.289:
|
||||
resolution: {integrity: sha512-ZHjSVpDa3D6izMq8/04lvkhkATUmL9px6ChPaXc1k6nU2Mrhlg1/7F0bdUqCwUjw3NsPTfPZsMDUU6ZIcRaeQw==}
|
||||
pdfjs-dist@6.4.299:
|
||||
resolution: {integrity: sha512-AVl138zALtfaAPvADulE0PZThbYzCBS79nL4pOSL/6Sm/4AH5A21BD9VHt97OlCuzJuCpmeZtAtkinisF4Vb1g==}
|
||||
engines: {node: '>=22.13.0 || >=24'}
|
||||
|
||||
picocolors@1.1.1:
|
||||
@@ -5554,52 +5554,52 @@ snapshots:
|
||||
'@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-android-arm64@1.0.9':
|
||||
'@napi-rs/canvas-android-arm64@1.0.13':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-darwin-arm64@1.0.9':
|
||||
'@napi-rs/canvas-darwin-arm64@1.0.13':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-darwin-x64@1.0.9':
|
||||
'@napi-rs/canvas-darwin-x64@1.0.13':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-linux-arm-gnueabihf@1.0.9':
|
||||
'@napi-rs/canvas-linux-arm-gnueabihf@1.0.13':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-linux-arm64-gnu@1.0.9':
|
||||
'@napi-rs/canvas-linux-arm64-gnu@1.0.13':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-linux-arm64-musl@1.0.9':
|
||||
'@napi-rs/canvas-linux-arm64-musl@1.0.13':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-linux-riscv64-gnu@1.0.9':
|
||||
'@napi-rs/canvas-linux-riscv64-gnu@1.0.13':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-linux-x64-gnu@1.0.9':
|
||||
'@napi-rs/canvas-linux-x64-gnu@1.0.13':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-linux-x64-musl@1.0.9':
|
||||
'@napi-rs/canvas-linux-x64-musl@1.0.13':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-win32-arm64-msvc@1.0.9':
|
||||
'@napi-rs/canvas-win32-arm64-msvc@1.0.13':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas-win32-x64-msvc@1.0.9':
|
||||
'@napi-rs/canvas-win32-x64-msvc@1.0.13':
|
||||
optional: true
|
||||
|
||||
'@napi-rs/canvas@1.0.9':
|
||||
'@napi-rs/canvas@1.0.13':
|
||||
optionalDependencies:
|
||||
'@napi-rs/canvas-android-arm64': 1.0.9
|
||||
'@napi-rs/canvas-darwin-arm64': 1.0.9
|
||||
'@napi-rs/canvas-darwin-x64': 1.0.9
|
||||
'@napi-rs/canvas-linux-arm-gnueabihf': 1.0.9
|
||||
'@napi-rs/canvas-linux-arm64-gnu': 1.0.9
|
||||
'@napi-rs/canvas-linux-arm64-musl': 1.0.9
|
||||
'@napi-rs/canvas-linux-riscv64-gnu': 1.0.9
|
||||
'@napi-rs/canvas-linux-x64-gnu': 1.0.9
|
||||
'@napi-rs/canvas-linux-x64-musl': 1.0.9
|
||||
'@napi-rs/canvas-win32-arm64-msvc': 1.0.9
|
||||
'@napi-rs/canvas-win32-x64-msvc': 1.0.9
|
||||
'@napi-rs/canvas-android-arm64': 1.0.13
|
||||
'@napi-rs/canvas-darwin-arm64': 1.0.13
|
||||
'@napi-rs/canvas-darwin-x64': 1.0.13
|
||||
'@napi-rs/canvas-linux-arm-gnueabihf': 1.0.13
|
||||
'@napi-rs/canvas-linux-arm64-gnu': 1.0.13
|
||||
'@napi-rs/canvas-linux-arm64-musl': 1.0.13
|
||||
'@napi-rs/canvas-linux-riscv64-gnu': 1.0.13
|
||||
'@napi-rs/canvas-linux-x64-gnu': 1.0.13
|
||||
'@napi-rs/canvas-linux-x64-musl': 1.0.13
|
||||
'@napi-rs/canvas-win32-arm64-msvc': 1.0.13
|
||||
'@napi-rs/canvas-win32-x64-msvc': 1.0.13
|
||||
optional: true
|
||||
|
||||
'@napi-rs/nice-android-arm-eabi@1.1.1':
|
||||
@@ -7800,9 +7800,9 @@ snapshots:
|
||||
lru-cache: 11.5.3
|
||||
minipass: 7.1.3
|
||||
|
||||
pdfjs-dist@6.3.289:
|
||||
pdfjs-dist@6.4.299:
|
||||
optionalDependencies:
|
||||
'@napi-rs/canvas': 1.0.9
|
||||
'@napi-rs/canvas': 1.0.13
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
|
||||
@@ -485,7 +485,7 @@
|
||||
</div>
|
||||
}
|
||||
<div class="input-group input-group-sm w-auto flex-nowrap">
|
||||
<button class="btn btn-outline-secondary" (click)="decreaseZoom()" aria-label="Zoom out" i18n-aria-label>
|
||||
<button class="btn btn-outline-secondary" type="button" (click)="decreaseZoom()" aria-label="Zoom out" i18n-aria-label>
|
||||
<i-bs width="1em" height="1em" name="dash"></i-bs>
|
||||
</button>
|
||||
<select class="form-select" (change)="setZoom($event.target.value)" [ngModelOptions]="{standalone: true}" [ngModel]="currentZoom" aria-label="Zoom" i18n-aria-label>
|
||||
@@ -495,7 +495,7 @@
|
||||
</option>
|
||||
}
|
||||
</select>
|
||||
<button class="btn btn-outline-secondary" (click)="increaseZoom()" aria-label="Zoom in" i18n-aria-label>
|
||||
<button class="btn btn-outline-secondary" type="button" (click)="increaseZoom()" aria-label="Zoom in" i18n-aria-label>
|
||||
<i-bs width="1em" height="1em" name="plus"></i-bs>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
+2
@@ -47,6 +47,8 @@
|
||||
<span>{{ change.key | titlecase }}</span>:
|
||||
@if (change.key === 'content') {
|
||||
<code class="text-primary">{{ change.value[1]?.substring(0,100) }}...</code>
|
||||
} @else if (change.key === 'tags') {
|
||||
<code class="text-primary">{{ getTagNames($any(change.value[1])) | async }}</code>
|
||||
} @else {
|
||||
<code class="text-primary">{{ getPrettyName(change.key, change.value[1]) | async }}</code>
|
||||
}
|
||||
|
||||
+34
@@ -12,6 +12,7 @@ import { CorrespondentService } from 'src/app/services/rest/correspondent.servic
|
||||
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
|
||||
import { DocumentService } from 'src/app/services/rest/document.service'
|
||||
import { StoragePathService } from 'src/app/services/rest/storage-path.service'
|
||||
import { TagService } from 'src/app/services/rest/tag.service'
|
||||
import { UserService } from 'src/app/services/rest/user.service'
|
||||
import { DocumentHistoryComponent } from './document-history.component'
|
||||
|
||||
@@ -174,6 +175,39 @@ describe('DocumentHistoryComponent', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should render bulk tag history with names and preserve missing tag IDs', () => {
|
||||
const getCachedSpy = jest
|
||||
.spyOn(TestBed.inject(TagService), 'getCached')
|
||||
.mockImplementation((id) =>
|
||||
of(id === 1 ? { id: 1, name: 'Invoice' } : null)
|
||||
)
|
||||
component.entries.set([
|
||||
{
|
||||
id: 1,
|
||||
action: AuditLogAction.Update,
|
||||
timestamp: '2021-01-01T00:00:00Z',
|
||||
remote_addr: null,
|
||||
changes: JSON.parse('{"tags":[[],[1,2345]]}'),
|
||||
},
|
||||
])
|
||||
component.loading.set(false)
|
||||
fixture.detectChanges()
|
||||
|
||||
expect(fixture.nativeElement.querySelector('code').textContent).toBe(
|
||||
'Invoice, 2345'
|
||||
)
|
||||
fixture.detectChanges()
|
||||
expect(getCachedSpy).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('should handle removal of all tags without a lookup', () => {
|
||||
const getCachedSpy = jest.spyOn(TestBed.inject(TagService), 'getCached')
|
||||
component.getTagNames([]).subscribe((result) => {
|
||||
expect(result).toBe('')
|
||||
})
|
||||
expect(getCachedSpy).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('getPrettyName should return the value as is for other types', () => {
|
||||
const id = '123'
|
||||
component.getPrettyName('other', id).subscribe((result) => {
|
||||
|
||||
+27
-1
@@ -2,7 +2,7 @@ import { AsyncPipe, KeyValuePipe, TitleCasePipe } from '@angular/common'
|
||||
import { Component, Input, OnInit, inject, signal } from '@angular/core'
|
||||
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||
import { Observable, first, map, of, shareReplay } from 'rxjs'
|
||||
import { Observable, first, forkJoin, map, of, shareReplay } from 'rxjs'
|
||||
import { AuditLogAction, AuditLogEntry } from 'src/app/data/auditlog-entry'
|
||||
import { DataType } from 'src/app/data/datatype'
|
||||
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
|
||||
@@ -10,6 +10,7 @@ import { CorrespondentService } from 'src/app/services/rest/correspondent.servic
|
||||
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
|
||||
import { DocumentService } from 'src/app/services/rest/document.service'
|
||||
import { StoragePathService } from 'src/app/services/rest/storage-path.service'
|
||||
import { TagService } from 'src/app/services/rest/tag.service'
|
||||
import { UserService } from 'src/app/services/rest/user.service'
|
||||
|
||||
@Component({
|
||||
@@ -30,6 +31,7 @@ export class DocumentHistoryComponent implements OnInit {
|
||||
private readonly correspondentService = inject(CorrespondentService)
|
||||
private readonly storagePathService = inject(StoragePathService)
|
||||
private readonly documentTypeService = inject(DocumentTypeService)
|
||||
private readonly tagService = inject(TagService)
|
||||
private readonly userService = inject(UserService)
|
||||
|
||||
public AuditLogAction = AuditLogAction
|
||||
@@ -63,6 +65,30 @@ export class DocumentHistoryComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
getTagNames(tagIds: number[]): Observable<string> {
|
||||
const cacheKey = `tags:${JSON.stringify(tagIds)}`
|
||||
const cachedNames = this.prettyNameCache.get(cacheKey)
|
||||
if (cachedNames) {
|
||||
return cachedNames
|
||||
}
|
||||
|
||||
const tagNames$ = tagIds.length
|
||||
? forkJoin(
|
||||
tagIds.map((tagId) =>
|
||||
this.tagService.getCached(tagId).pipe(
|
||||
first(),
|
||||
map((tag) => tag?.name ?? String(tagId))
|
||||
)
|
||||
)
|
||||
).pipe(map((names) => names.join(', ')))
|
||||
: of('')
|
||||
const names$ = tagNames$.pipe(
|
||||
shareReplay({ bufferSize: 1, refCount: true })
|
||||
)
|
||||
this.prettyNameCache.set(cacheKey, names$)
|
||||
return names$
|
||||
}
|
||||
|
||||
getPrettyName(type: DataType | string, id: string): Observable<string> {
|
||||
const cacheKey = `${type}:${id}`
|
||||
const cached = this.prettyNameCache.get(cacheKey)
|
||||
|
||||
Reference in new issue
Block a user