From d0ce4113e0d5256f4af7bc91e14dbc03e682e4a8 Mon Sep 17 00:00:00 2001 From: Trenton H <797416+stumpylog@users.noreply.github.com> Date: Sun, 28 Jan 2024 12:51:00 -0800 Subject: [PATCH 01/41] Resets -dev version string --- src-ui/src/environments/environment.prod.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-ui/src/environments/environment.prod.ts b/src-ui/src/environments/environment.prod.ts index 8de4d6400..6135915d9 100644 --- a/src-ui/src/environments/environment.prod.ts +++ b/src-ui/src/environments/environment.prod.ts @@ -5,7 +5,7 @@ export const environment = { apiBaseUrl: document.baseURI + 'api/', apiVersion: '4', appTitle: 'Paperless-ngx', - version: '2.4.3', + version: '2.4.3-dev', webSocketHost: window.location.host, webSocketProtocol: window.location.protocol == 'https:' ? 'wss:' : 'ws:', webSocketBaseUrl: base_url.pathname + 'ws/', From ae05011062fea40a02dac7474f0c033c54b3e79b Mon Sep 17 00:00:00 2001 From: Michael Wieland Date: Mon, 29 Jan 2024 20:07:40 +0100 Subject: [PATCH 02/41] Documentation: Add docs about missing timezones in MySQL/MariaDB (#5583) --------- Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com> --- docs/advanced_usage.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/advanced_usage.md b/docs/advanced_usage.md index 1d76b10d6..04626fe41 100644 --- a/docs/advanced_usage.md +++ b/docs/advanced_usage.md @@ -517,6 +517,18 @@ existing tables) with: an older system may fix issues that can arise while setting up Paperless-ngx but `utf8mb3` can cause issues with consumption (where `utf8mb4` does not). +### Missing timezones + +MySQL as well as MariaDB do not have any timezone information by default (though some +docker images such as the official MariaDB image take care of this for you) which will +cause unexpected behavior with date-based queries. + +To fix this, execute one of the following commands: + +MySQL: `mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql -p` + +MariaDB: `mariadb-tzinfo-to-sql /usr/share/zoneinfo | mariadb -u root mysql -p` + ## Barcodes {#barcodes} Paperless is able to utilize barcodes for automatically performing some tasks. From 2c8fddb554c79fdf80cb3574ac5cb7894e801140 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Jan 2024 00:10:12 +0000 Subject: [PATCH 03/41] Chore(deps): Bump the actions group with 1 update (#5597) Bumps the actions group with 1 update: [actions/cache](https://github.com/actions/cache). Updates `actions/cache` from 3 to 4 - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 32f6881aa..ec8407611 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -184,7 +184,7 @@ jobs: cache-dependency-path: 'src-ui/package-lock.json' - name: Cache frontend dependencies id: cache-frontend-deps - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.npm @@ -221,7 +221,7 @@ jobs: cache-dependency-path: 'src-ui/package-lock.json' - name: Cache frontend dependencies id: cache-frontend-deps - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.npm From 4996b7e5f75dc1807a43748b4edef530718b731e Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Tue, 30 Jan 2024 22:06:22 -0800 Subject: [PATCH 04/41] Correct position of text filter clear button --- .../document-list/filter-editor/filter-editor.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.html b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.html index 54427ad31..08f680b07 100644 --- a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.html +++ b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.html @@ -18,7 +18,7 @@ } @if (_textFilter) { - } From 5e3d1b26e759185a4286249f90194adafe4ef2bf Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 1 Feb 2024 01:20:14 -0800 Subject: [PATCH 05/41] Fix: Dont attempt to retrieve objects for which user doesnt have global permissions (#5612) --- ...permissions-filter-dropdown.component.html | 34 ++++--- .../permissions-filter-dropdown.component.ts | 2 +- .../saved-view-widget.component.html | 24 +++-- .../saved-view-widget.component.ts | 4 +- .../document-detail.component.spec.ts | 28 +++++- .../document-detail.component.ts | 63 ++++++++---- .../bulk-editor/bulk-editor.component.html | 98 ++++++++++--------- .../bulk-editor/bulk-editor.component.spec.ts | 18 ++++ .../bulk-editor/bulk-editor.component.ts | 60 +++++++++--- .../document-card-small.component.ts | 2 +- .../filter-editor.component.html | 36 ++++--- .../filter-editor.component.spec.ts | 39 +++++++- .../filter-editor/filter-editor.component.ts | 70 ++++++++++--- .../src/app/services/rest/document.service.ts | 40 +++++++- 14 files changed, 375 insertions(+), 143 deletions(-) diff --git a/src-ui/src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.html b/src-ui/src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.html index f95434c8f..d20986c57 100644 --- a/src-ui/src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.html +++ b/src-ui/src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.html @@ -62,22 +62,24 @@ } -
- - -
+ @if (permissionsService.currentUserCan(PermissionAction.View, PermissionType.User)) { +
+ + +
+ } @if (selectionModel.ownerFilter === OwnerFilterType.NONE || selectionModel.ownerFilter === OwnerFilterType.NOT_SELF) {
diff --git a/src-ui/src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.ts b/src-ui/src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.ts index 3f5c3e68d..b0c3e8817 100644 --- a/src-ui/src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.ts +++ b/src-ui/src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.ts @@ -67,7 +67,7 @@ export class PermissionsFilterDropdownComponent extends ComponentWithPermissions } constructor( - permissionsService: PermissionsService, + public permissionsService: PermissionsService, userService: UserService, private settingsService: SettingsService ) { diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html index 0a7a852ed..de46991d2 100644 --- a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html +++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html @@ -15,8 +15,14 @@ Created Title - Tags - Correspondent + @if (permissionsService.currentUserCan(PermissionAction.View, PermissionType.Tag)) { + Tags + } + @if (permissionsService.currentUserCan(PermissionAction.View, PermissionType.Correspondent)) { + Correspondent + } @else { + + } @@ -26,13 +32,15 @@ {{doc.title | documentTitle}} - - @for (t of doc.tags$ | async; track t) { - - } - + @if (permissionsService.currentUserCan(PermissionAction.View, PermissionType.Tag)) { + + @for (t of doc.tags$ | async; track t) { + + } + + } - @if (doc.correspondent !== null) { + @if (permissionsService.currentUserCan(PermissionAction.View, PermissionType.Correspondent) && doc.correspondent !== null) { {{(doc.correspondent$ | async)?.name}} }
diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts index aa1b160cf..c81ea5484 100644 --- a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts +++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts @@ -22,6 +22,7 @@ import { DocumentListViewService } from 'src/app/services/document-list-view.ser import { ComponentWithPermissions } from 'src/app/components/with-permissions/with-permissions.component' import { NgbPopover } from '@ng-bootstrap/ng-bootstrap' import { queryParamsFromFilterRules } from 'src/app/utils/query-params' +import { PermissionsService } from 'src/app/services/permissions.service' @Component({ selector: 'pngx-saved-view-widget', @@ -40,7 +41,8 @@ export class SavedViewWidgetComponent private list: DocumentListViewService, private consumerStatusService: ConsumerStatusService, public openDocumentsService: OpenDocumentsService, - public documentListViewService: DocumentListViewService + public documentListViewService: DocumentListViewService, + public permissionsService: PermissionsService ) { super() } diff --git a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts index af0e0e78e..a30588970 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts @@ -1,5 +1,8 @@ import { DatePipe } from '@angular/common' -import { HttpClientTestingModule } from '@angular/common/http/testing' +import { + HttpClientTestingModule, + HttpTestingController, +} from '@angular/common/http/testing' import { ComponentFixture, TestBed, @@ -71,6 +74,7 @@ import { CustomFieldDataType } from 'src/app/data/custom-field' import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service' import { PdfViewerComponent } from '../common/pdf-viewer/pdf-viewer.component' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' +import { environment } from 'src/environments/environment' const doc: Document = { id: 3, @@ -136,6 +140,7 @@ describe('DocumentDetailComponent', () => { let documentListViewService: DocumentListViewService let settingsService: SettingsService let customFieldsService: CustomFieldsService + let httpTestingController: HttpTestingController let currentUserCan = true let currentUserHasObjectPermissions = true @@ -266,6 +271,7 @@ describe('DocumentDetailComponent', () => { settingsService.currentUser = { id: 1 } customFieldsService = TestBed.inject(CustomFieldsService) fixture = TestBed.createComponent(DocumentDetailComponent) + httpTestingController = TestBed.inject(HttpTestingController) component = fixture.componentInstance }) @@ -350,6 +356,26 @@ describe('DocumentDetailComponent', () => { expect(component.documentForm.disabled).toBeTruthy() }) + it('should not attempt to retrieve objects if user does not have permissions', () => { + currentUserCan = false + initNormally() + expect(component.correspondents).toBeUndefined() + expect(component.documentTypes).toBeUndefined() + expect(component.storagePaths).toBeUndefined() + expect(component.users).toBeUndefined() + httpTestingController.expectNone(`${environment.apiBaseUrl}documents/tags/`) + httpTestingController.expectNone( + `${environment.apiBaseUrl}documents/correspondents/` + ) + httpTestingController.expectNone( + `${environment.apiBaseUrl}documents/document_types/` + ) + httpTestingController.expectNone( + `${environment.apiBaseUrl}documents/storage_paths/` + ) + currentUserCan = true + }) + it('should support creating document type', () => { initNormally() let openModal: NgbModalRef diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts index 0ce9fa007..0ca458a21 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.ts @@ -250,25 +250,50 @@ export class DocumentDetailComponent Object.assign(this.document, docValues) }) - this.correspondentService - .listAll() - .pipe(first(), takeUntil(this.unsubscribeNotifier)) - .subscribe((result) => (this.correspondents = result.results)) - - this.documentTypeService - .listAll() - .pipe(first(), takeUntil(this.unsubscribeNotifier)) - .subscribe((result) => (this.documentTypes = result.results)) - - this.storagePathService - .listAll() - .pipe(first(), takeUntil(this.unsubscribeNotifier)) - .subscribe((result) => (this.storagePaths = result.results)) - - this.userService - .listAll() - .pipe(first(), takeUntil(this.unsubscribeNotifier)) - .subscribe((result) => (this.users = result.results)) + if ( + this.permissionsService.currentUserCan( + PermissionAction.View, + PermissionType.Correspondent + ) + ) { + this.correspondentService + .listAll() + .pipe(first(), takeUntil(this.unsubscribeNotifier)) + .subscribe((result) => (this.correspondents = result.results)) + } + if ( + this.permissionsService.currentUserCan( + PermissionAction.View, + PermissionType.DocumentType + ) + ) { + this.documentTypeService + .listAll() + .pipe(first(), takeUntil(this.unsubscribeNotifier)) + .subscribe((result) => (this.documentTypes = result.results)) + } + if ( + this.permissionsService.currentUserCan( + PermissionAction.View, + PermissionType.StoragePath + ) + ) { + this.storagePathService + .listAll() + .pipe(first(), takeUntil(this.unsubscribeNotifier)) + .subscribe((result) => (this.storagePaths = result.results)) + } + if ( + this.permissionsService.currentUserCan( + PermissionAction.View, + PermissionType.User + ) + ) { + this.userService + .listAll() + .pipe(first(), takeUntil(this.unsubscribeNotifier)) + .subscribe((result) => (this.users = result.results)) + } this.getCustomFields() diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.html b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.html index b101c2742..0c261df67 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.html +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.html @@ -17,51 +17,59 @@
- - - - - - - - + @if (permissionService.currentUserCan(PermissionAction.View, PermissionType.Tag)) { + + + } + @if (permissionService.currentUserCan(PermissionAction.View, PermissionType.Correspondent)) { + + + } + @if (permissionService.currentUserCan(PermissionAction.View, PermissionType.DocumentType)) { + + + } + @if (permissionService.currentUserCan(PermissionAction.View, PermissionType.StoragePath)) { + + + }
diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts index af41d298c..8edfcc7e1 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts @@ -868,4 +868,22 @@ describe('BulkEditorComponent', () => { `${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id` ) // listAllFilteredIds }) + + it('should not attempt to retrieve objects if user does not have permissions', () => { + jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) + expect(component.tags).toBeUndefined() + expect(component.correspondents).toBeUndefined() + expect(component.documentTypes).toBeUndefined() + expect(component.storagePaths).toBeUndefined() + httpTestingController.expectNone(`${environment.apiBaseUrl}documents/tags/`) + httpTestingController.expectNone( + `${environment.apiBaseUrl}documents/correspondents/` + ) + httpTestingController.expectNone( + `${environment.apiBaseUrl}documents/document_types/` + ) + httpTestingController.expectNone( + `${environment.apiBaseUrl}documents/storage_paths/` + ) + }) }) diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts index 91b714b24..1c7e0b9c6 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -115,22 +115,50 @@ export class BulkEditorComponent } ngOnInit() { - this.tagService - .listAll() - .pipe(first()) - .subscribe((result) => (this.tags = result.results)) - this.correspondentService - .listAll() - .pipe(first()) - .subscribe((result) => (this.correspondents = result.results)) - this.documentTypeService - .listAll() - .pipe(first()) - .subscribe((result) => (this.documentTypes = result.results)) - this.storagePathService - .listAll() - .pipe(first()) - .subscribe((result) => (this.storagePaths = result.results)) + if ( + this.permissionService.currentUserCan( + PermissionAction.View, + PermissionType.Tag + ) + ) { + this.tagService + .listAll() + .pipe(first()) + .subscribe((result) => (this.tags = result.results)) + } + if ( + this.permissionService.currentUserCan( + PermissionAction.View, + PermissionType.Correspondent + ) + ) { + this.correspondentService + .listAll() + .pipe(first()) + .subscribe((result) => (this.correspondents = result.results)) + } + if ( + this.permissionService.currentUserCan( + PermissionAction.View, + PermissionType.DocumentType + ) + ) { + this.documentTypeService + .listAll() + .pipe(first()) + .subscribe((result) => (this.documentTypes = result.results)) + } + if ( + this.permissionService.currentUserCan( + PermissionAction.View, + PermissionType.StoragePath + ) + ) { + this.storagePathService + .listAll() + .pipe(first()) + .subscribe((result) => (this.storagePaths = result.results)) + } this.downloadForm .get('downloadFileTypeArchive') diff --git a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.ts b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.ts index bd565a9fb..2ca1a3408 100644 --- a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.ts +++ b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.ts @@ -79,7 +79,7 @@ export class DocumentCardSmallComponent extends ComponentWithPermissions { getTagsLimited$() { const limit = this.document.notes.length > 0 ? 6 : 7 - return this.document.tags$.pipe( + return this.document.tags$?.pipe( map((tags) => { if (tags.length > limit) { this.moreTags = tags.length - (limit - 1) diff --git a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.html b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.html index 08f680b07..89900e087 100644 --- a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.html +++ b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.html @@ -29,7 +29,8 @@
- - + } + @if (permissionsService.currentUserCan(PermissionAction.View, PermissionType.Correspondent)) { + - - + } + @if (permissionsService.currentUserCan(PermissionAction.View, PermissionType.DocumentType)) { + + } + @if (permissionsService.currentUserCan(PermissionAction.View, PermissionType.StoragePath)) { + + [allowSelectNone]="true"> + }
{ let fixture: ComponentFixture let documentService: DocumentService let settingsService: SettingsService + let permissionsService: PermissionsService + let httpTestingController: HttpTestingController beforeEach(fakeAsync(() => { TestBed.configureTestingModule({ @@ -199,6 +209,15 @@ describe('FilterEditorComponent', () => { documentService = TestBed.inject(DocumentService) settingsService = TestBed.inject(SettingsService) settingsService.currentUser = users[0] + permissionsService = TestBed.inject(PermissionsService) + jest + .spyOn(permissionsService, 'currentUserCan') + .mockImplementation((action, type) => { + // a little hack-ish, permissions filter dropdown causes reactive forms issue due to ng-select + // trying to apply formControlName + return type !== PermissionType.User + }) + httpTestingController = TestBed.inject(HttpTestingController) fixture = TestBed.createComponent(FilterEditorComponent) component = fixture.componentInstance component.filterRules = [] @@ -206,6 +225,24 @@ describe('FilterEditorComponent', () => { tick() })) + it('should not attempt to retrieve objects if user does not have permissions', () => { + jest.spyOn(permissionsService, 'currentUserCan').mockReset() + jest + .spyOn(permissionsService, 'currentUserCan') + .mockImplementation((action, type) => false) + component.ngOnInit() + httpTestingController.expectNone(`${environment.apiBaseUrl}documents/tags/`) + httpTestingController.expectNone( + `${environment.apiBaseUrl}documents/correspondents/` + ) + httpTestingController.expectNone( + `${environment.apiBaseUrl}documents/document_types/` + ) + httpTestingController.expectNone( + `${environment.apiBaseUrl}documents/storage_paths/` + ) + }) + // SET filterRules it('should ingest text filter rules for doc title', fakeAsync(() => { diff --git a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts index 03e1db539..b11874d7c 100644 --- a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts +++ b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts @@ -70,6 +70,12 @@ import { OwnerFilterType, PermissionsSelectionModel, } from '../../common/permissions-filter-dropdown/permissions-filter-dropdown.component' +import { + PermissionAction, + PermissionType, + PermissionsService, +} from 'src/app/services/permissions.service' +import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component' const TEXT_FILTER_TARGET_TITLE = 'title' const TEXT_FILTER_TARGET_TITLE_CONTENT = 'title-content' @@ -155,7 +161,10 @@ const DEFAULT_TEXT_FILTER_MODIFIER_OPTIONS = [ templateUrl: './filter-editor.component.html', styleUrls: ['./filter-editor.component.scss'], }) -export class FilterEditorComponent implements OnInit, OnDestroy { +export class FilterEditorComponent + extends ComponentWithPermissions + implements OnInit, OnDestroy +{ generateFilterName() { if (this.filterRules.length == 1) { let rule = this.filterRules[0] @@ -224,8 +233,11 @@ export class FilterEditorComponent implements OnInit, OnDestroy { private tagService: TagService, private correspondentService: CorrespondentService, private documentService: DocumentService, - private storagePathService: StoragePathService - ) {} + private storagePathService: StoragePathService, + public permissionsService: PermissionsService + ) { + super() + } @ViewChild('textFilterInput') textFilterInput: ElementRef @@ -872,18 +884,46 @@ export class FilterEditorComponent implements OnInit, OnDestroy { subscription: Subscription ngOnInit() { - this.tagService - .listAll() - .subscribe((result) => (this.tags = result.results)) - this.correspondentService - .listAll() - .subscribe((result) => (this.correspondents = result.results)) - this.documentTypeService - .listAll() - .subscribe((result) => (this.documentTypes = result.results)) - this.storagePathService - .listAll() - .subscribe((result) => (this.storagePaths = result.results)) + if ( + this.permissionsService.currentUserCan( + PermissionAction.View, + PermissionType.Tag + ) + ) { + this.tagService + .listAll() + .subscribe((result) => (this.tags = result.results)) + } + if ( + this.permissionsService.currentUserCan( + PermissionAction.View, + PermissionType.Correspondent + ) + ) { + this.correspondentService + .listAll() + .subscribe((result) => (this.correspondents = result.results)) + } + if ( + this.permissionsService.currentUserCan( + PermissionAction.View, + PermissionType.DocumentType + ) + ) { + this.documentTypeService + .listAll() + .subscribe((result) => (this.documentTypes = result.results)) + } + if ( + this.permissionsService.currentUserCan( + PermissionAction.View, + PermissionType.StoragePath + ) + ) { + this.storagePathService + .listAll() + .subscribe((result) => (this.storagePaths = result.results)) + } this.textFilterDebounce = new Subject() diff --git a/src-ui/src/app/services/rest/document.service.ts b/src-ui/src/app/services/rest/document.service.ts index ee0f26187..9ff99031f 100644 --- a/src-ui/src/app/services/rest/document.service.ts +++ b/src-ui/src/app/services/rest/document.service.ts @@ -13,6 +13,11 @@ import { TagService } from './tag.service' import { DocumentSuggestions } from 'src/app/data/document-suggestions' import { queryParamsFromFilterRules } from '../../utils/query-params' import { StoragePathService } from './storage-path.service' +import { + PermissionAction, + PermissionType, + PermissionsService, +} from '../permissions.service' export const DOCUMENT_SORT_FIELDS = [ { field: 'archive_serial_number', name: $localize`ASN` }, @@ -57,21 +62,40 @@ export class DocumentService extends AbstractPaperlessService { private correspondentService: CorrespondentService, private documentTypeService: DocumentTypeService, private tagService: TagService, - private storagePathService: StoragePathService + private storagePathService: StoragePathService, + private permissionsService: PermissionsService ) { super(http, 'documents') } addObservablesToDocument(doc: Document) { - if (doc.correspondent) { + if ( + doc.correspondent && + this.permissionsService.currentUserCan( + PermissionAction.View, + PermissionType.Correspondent + ) + ) { doc.correspondent$ = this.correspondentService.getCached( doc.correspondent ) } - if (doc.document_type) { + if ( + doc.document_type && + this.permissionsService.currentUserCan( + PermissionAction.View, + PermissionType.DocumentType + ) + ) { doc.document_type$ = this.documentTypeService.getCached(doc.document_type) } - if (doc.tags) { + if ( + doc.tags && + this.permissionsService.currentUserCan( + PermissionAction.View, + PermissionType.Tag + ) + ) { doc.tags$ = this.tagService .getCachedMany(doc.tags) .pipe( @@ -80,7 +104,13 @@ export class DocumentService extends AbstractPaperlessService { ) ) } - if (doc.storage_path) { + if ( + doc.storage_path && + this.permissionsService.currentUserCan( + PermissionAction.View, + PermissionType.StoragePath + ) + ) { doc.storage_path$ = this.storagePathService.getCached(doc.storage_path) } return doc From 38a817e8870971aac86fb5f4d743ed859f460ff7 Mon Sep 17 00:00:00 2001 From: Henning B <14870695+henningBunk@users.noreply.github.com> Date: Thu, 1 Feb 2024 20:25:57 +0100 Subject: [PATCH 06/41] Enhancement: Respect PDF cropbox for thumbnail generation (#5531) * Use the convert argument '-define "pdf:use-cropbox=true"' when creating thumbnails. That way cropboxes, if present, gets respected for thumbnail generation --- src/documents/parsers.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/documents/parsers.py b/src/documents/parsers.py index 12e5d6b33..b791d8322 100644 --- a/src/documents/parsers.py +++ b/src/documents/parsers.py @@ -140,6 +140,7 @@ def run_convert( type=None, depth=None, auto_orient=False, + use_cropbox=False, extra=None, logging_group=None, ) -> None: @@ -158,6 +159,7 @@ def run_convert( args += ["-type", str(type)] if type else [] args += ["-depth", str(depth)] if depth else [] args += ["-auto-orient"] if auto_orient else [] + args += ["-define", "pdf:use-cropbox=true"] if use_cropbox else [] args += [input_file, output_file] logger.debug("Execute: " + " ".join(args), extra={"group": logging_group}) @@ -229,6 +231,7 @@ def make_thumbnail_from_pdf(in_path, temp_dir, logging_group=None) -> str: strip=True, trim=False, auto_orient=True, + use_cropbox=True, input_file=f"{in_path}[0]", output_file=out_path, logging_group=logging_group, From 61209b10570b3da78f9b700f306e19ac5437b4cb Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 1 Feb 2024 11:46:26 -0800 Subject: [PATCH 07/41] Enhancement: re-implement remote user auth for API as opt-in (#5561) --- docs/api.md | 8 +- docs/configuration.md | 14 ++- src/paperless/auth.py | 8 ++ src/paperless/settings.py | 35 +++++--- src/paperless/tests/test_remote_user.py | 110 ++++++++++++++++++++++++ 5 files changed, 163 insertions(+), 12 deletions(-) create mode 100644 src/paperless/tests/test_remote_user.py diff --git a/docs/api.md b/docs/api.md index 97ccf4c3a..21dc00b47 100644 --- a/docs/api.md +++ b/docs/api.md @@ -139,7 +139,7 @@ document. Paperless only reports PDF metadata at this point. ## Authorization -The REST api provides three different forms of authentication. +The REST api provides four different forms of authentication. 1. Basic authentication @@ -177,6 +177,12 @@ The REST api provides three different forms of authentication. Tokens can also be managed in the Django admin. +4. Remote User authentication + + If enabled (see + [configuration](configuration.md#PAPERLESS_ENABLE_HTTP_REMOTE_USER_API)), + you can authenticate against the API using Remote User auth. + ## Searching for documents Full text searching is available on the `/api/documents/` endpoint. Two diff --git a/docs/configuration.md b/docs/configuration.md index b68198619..be363c269 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -462,9 +462,21 @@ applications. Defaults to "false" which disables this feature. +#### [`PAPERLESS_ENABLE_HTTP_REMOTE_USER_API=`](#PAPERLESS_ENABLE_HTTP_REMOTE_USER_API) {#PAPERLESS_ENABLE_HTTP_REMOTE_USER_API} + +: Allows authentication via HTTP_REMOTE_USER directly against the API + + !!! warning + + See the warning above about securing your installation when using remote user header authentication. This setting is separate from + `PAPERLESS_ENABLE_HTTP_REMOTE_USER` to avoid introducing a security vulnerability to existing reverse proxy setups. As above, + ensure that your reverse proxy does not simply pass the `Remote-User` header from the internet to paperless. + + Defaults to "false" which disables this feature. + #### [`PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME=`](#PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME) {#PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME} -: If "PAPERLESS_ENABLE_HTTP_REMOTE_USER" is enabled, this +: If "PAPERLESS_ENABLE_HTTP_REMOTE_USER" or `PAPERLESS_ENABLE_HTTP_REMOTE_USER_API` are enabled, this property allows to customize the name of the HTTP header from which the authenticated username is extracted. Values are in terms of [HttpRequest.META](https://docs.djangoproject.com/en/4.1/ref/request-response/#django.http.HttpRequest.META). diff --git a/src/paperless/auth.py b/src/paperless/auth.py index a23b01cb4..98e2a8b30 100644 --- a/src/paperless/auth.py +++ b/src/paperless/auth.py @@ -47,3 +47,11 @@ class HttpRemoteUserMiddleware(PersistentRemoteUserMiddleware): """ header = settings.HTTP_REMOTE_USER_HEADER_NAME + + +class PaperlessRemoteUserAuthentication(authentication.RemoteUserAuthentication): + """ + REMOTE_USER authentication for DRF which overrides the default header. + """ + + header = settings.HTTP_REMOTE_USER_HEADER_NAME diff --git a/src/paperless/settings.py b/src/paperless/settings.py index bc815d4d5..c9d5848c0 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -420,19 +420,34 @@ if AUTO_LOGIN_USERNAME: # regular login in case the provided user does not exist. MIDDLEWARE.insert(_index + 1, "paperless.auth.AutoLoginMiddleware") -ENABLE_HTTP_REMOTE_USER = __get_boolean("PAPERLESS_ENABLE_HTTP_REMOTE_USER") -HTTP_REMOTE_USER_HEADER_NAME = os.getenv( - "PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME", - "HTTP_REMOTE_USER", -) -if ENABLE_HTTP_REMOTE_USER: - MIDDLEWARE.append("paperless.auth.HttpRemoteUserMiddleware") - AUTHENTICATION_BACKENDS.insert(0, "django.contrib.auth.backends.RemoteUserBackend") - REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"].append( - "rest_framework.authentication.RemoteUserAuthentication", +def _parse_remote_user_settings() -> str: + global MIDDLEWARE, AUTHENTICATION_BACKENDS, REST_FRAMEWORK + enable = __get_boolean("PAPERLESS_ENABLE_HTTP_REMOTE_USER") + enable_api = __get_boolean("PAPERLESS_ENABLE_HTTP_REMOTE_USER_API") + if enable or enable_api: + MIDDLEWARE.append("paperless.auth.HttpRemoteUserMiddleware") + AUTHENTICATION_BACKENDS.insert( + 0, + "django.contrib.auth.backends.RemoteUserBackend", + ) + + if enable_api: + REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"].insert( + 0, + "paperless.auth.PaperlessRemoteUserAuthentication", + ) + + header_name = os.getenv( + "PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME", + "HTTP_REMOTE_USER", ) + return header_name + + +HTTP_REMOTE_USER_HEADER_NAME = _parse_remote_user_settings() + # X-Frame options for embedded PDF display: X_FRAME_OPTIONS = "ANY" if DEBUG else "SAMEORIGIN" diff --git a/src/paperless/tests/test_remote_user.py b/src/paperless/tests/test_remote_user.py new file mode 100644 index 000000000..c5d7a6db4 --- /dev/null +++ b/src/paperless/tests/test_remote_user.py @@ -0,0 +1,110 @@ +import os +from unittest import mock + +from django.contrib.auth.models import User +from rest_framework import status +from rest_framework.test import APITestCase + +from documents.tests.utils import DirectoriesMixin +from paperless.settings import _parse_remote_user_settings + + +class TestRemoteUser(DirectoriesMixin, APITestCase): + def setUp(self): + super().setUp() + + self.user = User.objects.create_superuser( + username="temp_admin", + ) + + def test_remote_user(self): + """ + GIVEN: + - Configured user + - Remote user auth is enabled + WHEN: + - Call is made to root + THEN: + - Call succeeds + """ + + with mock.patch.dict( + os.environ, + { + "PAPERLESS_ENABLE_HTTP_REMOTE_USER": "True", + }, + ): + _parse_remote_user_settings() + + response = self.client.get("/documents/") + + self.assertEqual( + response.status_code, + status.HTTP_302_FOUND, + ) + + response = self.client.get( + "/documents/", + headers={ + "Remote-User": self.user.username, + }, + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_remote_user_api(self): + """ + GIVEN: + - Configured user + - Remote user auth is enabled for the API + WHEN: + - API call is made to get documents + THEN: + - Call succeeds + """ + + with mock.patch.dict( + os.environ, + { + "PAPERLESS_ENABLE_HTTP_REMOTE_USER_API": "True", + }, + ): + _parse_remote_user_settings() + + response = self.client.get("/api/documents/") + + # 403 testing locally, 401 on ci... + self.assertIn( + response.status_code, + [status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN], + ) + + response = self.client.get( + "/api/documents/", + headers={ + "Remote-User": self.user.username, + }, + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_remote_user_header_setting(self): + """ + GIVEN: + - Remote user header name is set + WHEN: + - Settings are parsed + THEN: + - Correct header name is returned + """ + + with mock.patch.dict( + os.environ, + { + "PAPERLESS_ENABLE_HTTP_REMOTE_USER": "True", + "PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME": "HTTP_FOO", + }, + ): + header_name = _parse_remote_user_settings() + + self.assertEqual(header_name, "HTTP_FOO") From 8f3ab2791b31a34cc5473cb8160bff5b3a026200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=ED=86=A0=EB=B9=84?= Date: Thu, 1 Feb 2024 20:49:34 +0100 Subject: [PATCH 08/41] Documentation: fix more like parameter in API docs (#5624) --- docs/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api.md b/docs/api.md index 21dc00b47..2e91d4f2b 100644 --- a/docs/api.md +++ b/docs/api.md @@ -191,7 +191,7 @@ results: - `/api/documents/?query=your%20search%20query`: Search for a document using a full text query. For details on the syntax, see [Basic Usage - Searching](usage.md#basic-usage_searching). -- `/api/documents/?more_like=1234`: Search for documents similar to +- `/api/documents/?more_like_id=1234`: Search for documents similar to the document with id 1234. Pagination works exactly the same as it does for normal requests on this From 454098630b6df4048dc278d0f0dec7e0aa5e3c1a Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 1 Feb 2024 11:56:57 -0800 Subject: [PATCH 09/41] Enhancement: mergeable bulk edit permissions (#5508) --- docs/api.md | 68 +++++++++++ src-ui/messages.xlf | 34 ++++-- .../common/input/switch/switch.component.html | 2 +- .../permissions-dialog.component.html | 15 ++- .../permissions-dialog.component.spec.ts | 21 ++++ .../permissions-dialog.component.ts | 20 +++- .../bulk-editor/bulk-editor.component.spec.ts | 20 +++- .../bulk-editor/bulk-editor.component.ts | 13 ++- .../manage/mail/mail.component.spec.ts | 9 +- .../components/manage/mail/mail.component.ts | 37 +++--- .../management-list.component.spec.ts | 10 +- .../management-list.component.ts | 5 +- .../rest/abstract-name-filter-service.spec.ts | 12 +- .../rest/abstract-name-filter-service.ts | 4 +- src/documents/bulk_edit.py | 10 +- src/documents/serialisers.py | 8 ++ src/documents/test_bulk_edit.py | 110 ++++++++++++++++++ src/documents/tests/test_api_bulk_edit.py | 52 +++++++++ src/documents/tests/test_api_permissions.py | 77 +++++++++++- src/documents/views.py | 16 ++- 20 files changed, 479 insertions(+), 64 deletions(-) create mode 100644 src/documents/test_bulk_edit.py diff --git a/docs/api.md b/docs/api.md index 2e91d4f2b..bd5154ada 100644 --- a/docs/api.md +++ b/docs/api.md @@ -330,6 +330,64 @@ granted). You can pass the parameter `full_perms=true` to API calls to view the full permissions of objects in a format that mirrors the `set_permissions` parameter above. +## Bulk Editing + +The API supports various bulk-editing operations which are executed asynchronously. + +### Documents + +For bulk operations on documents, use the endpoint `/api/bulk_edit/` which accepts +a json payload of the format: + +```json +{ + "documents": [LIST_OF_DOCUMENT_IDS], + "method": METHOD, // see below + "parameters": args // see below +} +``` + +The following methods are supported: + +- `set_correspondent` + - Requires `parameters`: `{ "correspondent": CORRESPONDENT_ID }` +- `set_document_type` + - Requires `parameters`: `{ "document_type": DOCUMENT_TYPE_ID }` +- `set_storage_path` + - Requires `parameters`: `{ "storage_path": STORAGE_PATH_ID }` +- `add_tag` + - Requires `parameters`: `{ "tag": TAG_ID }` +- `remove_tag` + - Requires `parameters`: `{ "tag": TAG_ID }` +- `modify_tags` + - Requires `parameters`: `{ "add_tags": [LIST_OF_TAG_IDS] }` and / or `{ "remove_tags": [LIST_OF_TAG_IDS] }` +- `delete` + - No `parameters` required +- `redo_ocr` + - No `parameters` required +- `set_permissions` + - Requires `parameters`: + - `"permissions": PERMISSIONS_OBJ` (see format [above](#permissions)) and / or + - `"owner": OWNER_ID or null` + - `"merge": true or false` (defaults to false) + - The `merge` flag determines if the supplied permissions will overwrite all existing permissions (including + removing them) or be merged with existing permissions. + +### Objects + +Bulk editing for objects (tags, document types etc.) currently supports only updating permissions, using +the endpoint: `/api/bulk_edit_object_perms/` which requires a json payload of the format: + +```json +{ + "objects": [LIST_OF_OBJECT_IDS], + "object_type": "tags", "correspondents", "document_types" or "storage_paths" + "owner": OWNER_ID // optional + "permissions": { "view": { "users": [] ... }, "change": { ... } }, // (see 'set_permissions' format above) + "merge": true / false // defaults to false, see above +} +``` + ## API Versioning The REST API is versioned since Paperless-ngx 1.3.0. @@ -386,3 +444,13 @@ Initial API version. color to use for a specific tag, which is either black or white depending on the brightness of `Tag.color`. - Removed field `Tag.colour`. + +#### Version 3 + +- Permissions endpoints have been added. +- The format of the `/api/ui_settings/` has changed. + +#### Version 4 + +- Consumption templates were refactored to workflows and API endpoints + changed as such. diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index e7263e0f2..56866f512 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -620,7 +620,7 @@ src/app/components/common/permissions-dialog/permissions-dialog.component.html - 20 + 23 src/app/components/dashboard/dashboard.component.html @@ -2438,7 +2438,7 @@ src/app/components/common/permissions-dialog/permissions-dialog.component.html - 23 + 26 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -2505,7 +2505,7 @@ src/app/components/common/permissions-dialog/permissions-dialog.component.html - 22 + 25 src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html @@ -3923,6 +3923,13 @@ 15 + + Merge with existing permissions + + src/app/components/common/permissions-dialog/permissions-dialog.component.html + 14 + + Set permissions @@ -3937,11 +3944,18 @@ 33 - - Note that permissions set here will override any existing permissions + + Existing owner, user and group permissions will be merged with these settings. src/app/components/common/permissions-dialog/permissions-dialog.component.ts - 71 + 74 + + + + Any and all existing owner, user and group permissions will be replaced. + + src/app/components/common/permissions-dialog/permissions-dialog.component.ts + 75 @@ -6100,18 +6114,18 @@ Permissions updated src/app/components/manage/mail/mail.component.ts - 211 + 212 Error updating permissions src/app/components/manage/mail/mail.component.ts - 215 + 217 src/app/components/manage/management-list/management-list.component.ts - 300 + 301 @@ -6277,7 +6291,7 @@ Permissions updated successfully src/app/components/manage/management-list/management-list.component.ts - 293 + 294 diff --git a/src-ui/src/app/components/common/input/switch/switch.component.html b/src-ui/src/app/components/common/input/switch/switch.component.html index 489106fcb..5acef7a75 100644 --- a/src-ui/src/app/components/common/input/switch/switch.component.html +++ b/src-ui/src/app/components/common/input/switch/switch.component.html @@ -15,7 +15,7 @@ }
} -
+
@if (horizontal) { diff --git a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.html b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.html index 37ab7efbd..fd88002ba 100644 --- a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.html +++ b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.html @@ -5,12 +5,15 @@
@@ -20,5 +23,5 @@ Loading... } - +
diff --git a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.spec.ts b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.spec.ts index 3f601d771..bc8ccdecb 100644 --- a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.spec.ts @@ -11,6 +11,7 @@ import { NgSelectModule } from '@ng-select/ng-select' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { PermissionsUserComponent } from '../input/permissions/permissions-user/permissions-user.component' import { PermissionsGroupComponent } from '../input/permissions/permissions-group/permissions-group.component' +import { SwitchComponent } from '../input/switch/switch.component' const set_permissions = { owner: 10, @@ -37,6 +38,7 @@ describe('PermissionsDialogComponent', () => { PermissionsDialogComponent, SafeHtmlPipe, SelectComponent, + SwitchComponent, PermissionsFormComponent, PermissionsUserComponent, PermissionsGroupComponent, @@ -112,4 +114,23 @@ describe('PermissionsDialogComponent', () => { expect(component.title).toEqual(`Edit permissions for ${obj.name}`) expect(component.permissions).toEqual(set_permissions) }) + + it('should toggle hint based on object existence (if editing) or merge flag', () => { + component.form.get('merge').setValue(true) + expect(component.hint.includes('Existing')).toBeTruthy() + component.form.get('merge').setValue(false) + expect(component.hint.includes('will be replaced')).toBeTruthy() + component.object = {} + expect(component.hint).toBeNull() + }) + + it('should emit permissions and merge flag on confirm', () => { + const confirmSpy = jest.spyOn(component.confirmClicked, 'emit') + component.form.get('permissions_form').setValue(set_permissions) + component.confirm() + expect(confirmSpy).toHaveBeenCalledWith({ + permissions: set_permissions, + merge: true, + }) + }) }) diff --git a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.ts b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.ts index 9a514387c..afe1bebb3 100644 --- a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.ts +++ b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.ts @@ -32,6 +32,7 @@ export class PermissionsDialogComponent { this.o = o this.title = $localize`Edit permissions for ` + o['name'] this.form.patchValue({ + merge: true, permissions_form: { owner: o.owner, set_permissions: o.permissions, @@ -43,8 +44,9 @@ export class PermissionsDialogComponent { return this.o } - form = new FormGroup({ + public form = new FormGroup({ permissions_form: new FormControl(), + merge: new FormControl(true), }) buttonsEnabled: boolean = true @@ -66,11 +68,21 @@ export class PermissionsDialogComponent { } } - @Input() - message = - $localize`Note that permissions set here will override any existing permissions` + get hint(): string { + if (this.object) return null + return this.form.get('merge').value + ? $localize`Existing owner, user and group permissions will be merged with these settings.` + : $localize`Any and all existing owner, user and group permissions will be replaced.` + } cancelClicked() { this.activeModal.close() } + + confirm() { + this.confirmClicked.emit({ + permissions: this.permissions, + merge: this.form.get('merge').value, + }) + } } diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts index 8edfcc7e1..42f8b6d1d 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts @@ -41,6 +41,7 @@ import { PermissionsUserComponent } from '../../common/input/permissions/permiss import { NgSelectModule } from '@ng-select/ng-select' import { GroupService } from 'src/app/services/rest/group.service' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' +import { SwitchComponent } from '../../common/input/switch/switch.component' const selectionData: SelectionData = { selected_tags: [ @@ -81,6 +82,7 @@ describe('BulkEditorComponent', () => { SelectComponent, PermissionsGroupComponent, PermissionsUserComponent, + SwitchComponent, ], providers: [ PermissionsService, @@ -851,7 +853,18 @@ describe('BulkEditorComponent', () => { fixture.detectChanges() component.setPermissions() expect(modal).not.toBeUndefined() - modal.componentInstance.confirmClicked.next() + const perms = { + permissions: { + view_users: [], + change_users: [], + view_groups: [], + change_groups: [], + }, + } + modal.componentInstance.confirmClicked.emit({ + permissions: perms, + merge: true, + }) let req = httpTestingController.expectOne( `${environment.apiBaseUrl}documents/bulk_edit/` ) @@ -859,7 +872,10 @@ describe('BulkEditorComponent', () => { expect(req.request.body).toEqual({ documents: [3, 4], method: 'set_permissions', - parameters: undefined, + parameters: { + permissions: perms.permissions, + merge: true, + }, }) httpTestingController.match( `${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true` diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts index 1c7e0b9c6..49d4c070f 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -540,9 +540,14 @@ export class BulkEditorComponent let modal = this.modalService.open(PermissionsDialogComponent, { backdrop: 'static', }) - modal.componentInstance.confirmClicked.subscribe((permissions) => { - modal.componentInstance.buttonsEnabled = false - this.executeBulkOperation(modal, 'set_permissions', permissions) - }) + modal.componentInstance.confirmClicked.subscribe( + ({ permissions, merge }) => { + modal.componentInstance.buttonsEnabled = false + this.executeBulkOperation(modal, 'set_permissions', { + ...permissions, + merge, + }) + } + ) } } diff --git a/src-ui/src/app/components/manage/mail/mail.component.spec.ts b/src-ui/src/app/components/manage/mail/mail.component.spec.ts index 5680d8faa..fcc5bcc6b 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.spec.ts +++ b/src-ui/src/app/components/manage/mail/mail.component.spec.ts @@ -41,6 +41,7 @@ import { TagsComponent } from '../../common/input/tags/tags.component' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' +import { SwitchComponent } from '../../common/input/switch/switch.component' const mailAccounts = [ { id: 1, name: 'account1' }, @@ -82,6 +83,7 @@ describe('MailComponent', () => { PermissionsGroupComponent, PermissionsDialogComponent, PermissionsFormComponent, + SwitchComponent, ], providers: [CustomDatePipe, DatePipe, PermissionsGuard], imports: [ @@ -267,11 +269,11 @@ describe('MailComponent', () => { rulePatchSpy.mockReturnValueOnce( throwError(() => new Error('error saving perms')) ) - dialog.confirmClicked.emit(perms) + dialog.confirmClicked.emit({ permissions: perms, merge: true }) expect(rulePatchSpy).toHaveBeenCalled() expect(toastErrorSpy).toHaveBeenCalled() rulePatchSpy.mockReturnValueOnce(of(mailRules[0] as MailRule)) - dialog.confirmClicked.emit(perms) + dialog.confirmClicked.emit({ permissions: perms, merge: true }) expect(toastInfoSpy).toHaveBeenCalledWith('Permissions updated') modalService.dismissAll() @@ -299,8 +301,7 @@ describe('MailComponent', () => { expect(modal).not.toBeUndefined() let dialog = modal.componentInstance as PermissionsDialogComponent expect(dialog.object).toEqual(mailAccounts[0]) - dialog = modal.componentInstance as PermissionsDialogComponent - dialog.confirmClicked.emit(perms) + dialog.confirmClicked.emit({ permissions: perms, merge: true }) expect(accountPatchSpy).toHaveBeenCalled() }) }) diff --git a/src-ui/src/app/components/manage/mail/mail.component.ts b/src-ui/src/app/components/manage/mail/mail.component.ts index 9bb33c03b..d8820ed38 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.ts +++ b/src-ui/src/app/components/manage/mail/mail.component.ts @@ -200,22 +200,27 @@ export class MailComponent const dialog: PermissionsDialogComponent = modal.componentInstance as PermissionsDialogComponent dialog.object = object - modal.componentInstance.confirmClicked.subscribe((permissions) => { - modal.componentInstance.buttonsEnabled = false - const service: AbstractPaperlessService = - 'account' in object ? this.mailRuleService : this.mailAccountService - object.owner = permissions['owner'] - object['set_permissions'] = permissions['set_permissions'] - service.patch(object).subscribe({ - next: () => { - this.toastService.showInfo($localize`Permissions updated`) - modal.close() - }, - error: (e) => { - this.toastService.showError($localize`Error updating permissions`, e) - }, - }) - }) + modal.componentInstance.confirmClicked.subscribe( + ({ permissions, merge }) => { + modal.componentInstance.buttonsEnabled = false + const service: AbstractPaperlessService = + 'account' in object ? this.mailRuleService : this.mailAccountService + object.owner = permissions['owner'] + object['set_permissions'] = permissions['set_permissions'] + service.patch(object).subscribe({ + next: () => { + this.toastService.showInfo($localize`Permissions updated`) + modal.close() + }, + error: (e) => { + this.toastService.showError( + $localize`Error updating permissions`, + e + ) + }, + }) + } + ) } userCanEdit(obj: ObjectWithPermissions): boolean { diff --git a/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts b/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts index 44a3452d7..6196a3c8a 100644 --- a/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts +++ b/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts @@ -264,13 +264,19 @@ describe('ManagementListComponent', () => { throwError(() => new Error('error setting permissions')) ) const errorToastSpy = jest.spyOn(toastService, 'showError') - modal.componentInstance.confirmClicked.emit() + modal.componentInstance.confirmClicked.emit({ + permissions: {}, + merge: true, + }) expect(bulkEditPermsSpy).toHaveBeenCalled() expect(errorToastSpy).toHaveBeenCalled() const successToastSpy = jest.spyOn(toastService, 'showInfo') bulkEditPermsSpy.mockReturnValueOnce(of('OK')) - modal.componentInstance.confirmClicked.emit() + modal.componentInstance.confirmClicked.emit({ + permissions: {}, + merge: true, + }) expect(bulkEditPermsSpy).toHaveBeenCalled() expect(successToastSpy).toHaveBeenCalled() }) diff --git a/src-ui/src/app/components/manage/management-list/management-list.component.ts b/src-ui/src/app/components/manage/management-list/management-list.component.ts index e20b5d4a7..a89b5e4f6 100644 --- a/src-ui/src/app/components/manage/management-list/management-list.component.ts +++ b/src-ui/src/app/components/manage/management-list/management-list.component.ts @@ -279,12 +279,13 @@ export abstract class ManagementListComponent backdrop: 'static', }) modal.componentInstance.confirmClicked.subscribe( - (permissions: { owner: number; set_permissions: PermissionsObject }) => { + ({ permissions, merge }) => { modal.componentInstance.buttonsEnabled = false this.service .bulk_update_permissions( Array.from(this.selectedObjects), - permissions + permissions, + merge ) .subscribe({ next: () => { diff --git a/src-ui/src/app/services/rest/abstract-name-filter-service.spec.ts b/src-ui/src/app/services/rest/abstract-name-filter-service.spec.ts index 70ae211e5..e09270701 100644 --- a/src-ui/src/app/services/rest/abstract-name-filter-service.spec.ts +++ b/src-ui/src/app/services/rest/abstract-name-filter-service.spec.ts @@ -53,10 +53,14 @@ export const commonAbstractNameFilterPaperlessServiceTests = ( }, } subscription = service - .bulk_update_permissions([1, 2], { - owner, - set_permissions: permissions, - }) + .bulk_update_permissions( + [1, 2], + { + owner, + set_permissions: permissions, + }, + true + ) .subscribe() const req = httpTestingController.expectOne( `${environment.apiBaseUrl}bulk_edit_object_perms/` diff --git a/src-ui/src/app/services/rest/abstract-name-filter-service.ts b/src-ui/src/app/services/rest/abstract-name-filter-service.ts index 5e0377cb9..b38994086 100644 --- a/src-ui/src/app/services/rest/abstract-name-filter-service.ts +++ b/src-ui/src/app/services/rest/abstract-name-filter-service.ts @@ -26,13 +26,15 @@ export abstract class AbstractNameFilterService< bulk_update_permissions( objects: Array, - permissions: { owner: number; set_permissions: PermissionsObject } + permissions: { owner: number; set_permissions: PermissionsObject }, + merge: boolean ): Observable { return this.http.post(`${this.baseUrl}bulk_edit_object_perms/`, { objects, object_type: this.resourceName, owner: permissions.owner, permissions: permissions.set_permissions, + merge, }) } } diff --git a/src/documents/bulk_edit.py b/src/documents/bulk_edit.py index ab0049eaa..ba001fd14 100644 --- a/src/documents/bulk_edit.py +++ b/src/documents/bulk_edit.py @@ -129,13 +129,17 @@ def redo_ocr(doc_ids): return "OK" -def set_permissions(doc_ids, set_permissions, owner=None): +def set_permissions(doc_ids, set_permissions, owner=None, merge=False): qs = Document.objects.filter(id__in=doc_ids) - qs.update(owner=owner) + if merge: + # If merging, only set owner for documents that don't have an owner + qs.filter(owner__isnull=True).update(owner=owner) + else: + qs.update(owner=owner) for doc in qs: - set_permissions_for_object(set_permissions, doc) + set_permissions_for_object(permissions=set_permissions, object=doc, merge=merge) affected_docs = [doc.id for doc in qs] diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 942d22fe5..3c65e11d9 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -916,6 +916,8 @@ class BulkEditSerializer(DocumentListSerializer, SetPermissionsMixin): ) if "owner" in parameters and parameters["owner"] is not None: self._validate_owner(parameters["owner"]) + if "merge" not in parameters: + parameters["merge"] = False def validate(self, attrs): method = attrs["method"] @@ -1258,6 +1260,12 @@ class BulkEditObjectPermissionsSerializer(serializers.Serializer, SetPermissions write_only=True, ) + merge = serializers.BooleanField( + default=False, + write_only=True, + required=False, + ) + def get_object_class(self, object_type): object_class = None if object_type == "tags": diff --git a/src/documents/test_bulk_edit.py b/src/documents/test_bulk_edit.py new file mode 100644 index 000000000..ad622ca24 --- /dev/null +++ b/src/documents/test_bulk_edit.py @@ -0,0 +1,110 @@ +from unittest import mock + +from django.contrib.auth.models import Group +from django.contrib.auth.models import User +from django.test import TestCase +from guardian.shortcuts import assign_perm +from guardian.shortcuts import get_groups_with_perms +from guardian.shortcuts import get_users_with_perms + +from documents.bulk_edit import set_permissions +from documents.models import Document +from documents.tests.utils import DirectoriesMixin + + +class TestBulkEditPermissions(DirectoriesMixin, TestCase): + def setUp(self): + super().setUp() + + self.doc1 = Document.objects.create(checksum="A", title="A") + self.doc2 = Document.objects.create(checksum="B", title="B") + self.doc3 = Document.objects.create(checksum="C", title="C") + + self.owner = User.objects.create(username="test_owner") + self.user1 = User.objects.create(username="user1") + self.user2 = User.objects.create(username="user2") + self.group1 = Group.objects.create(name="group1") + self.group2 = Group.objects.create(name="group2") + + @mock.patch("documents.tasks.bulk_update_documents.delay") + def test_set_permissions(self, m): + doc_ids = [self.doc1.id, self.doc2.id, self.doc3.id] + + assign_perm("view_document", self.group1, self.doc1) + + permissions = { + "view": { + "users": [self.user1.id, self.user2.id], + "groups": [self.group2.id], + }, + "change": { + "users": [self.user1.id], + "groups": [self.group2.id], + }, + } + + set_permissions( + doc_ids, + set_permissions=permissions, + owner=self.owner, + merge=False, + ) + m.assert_called_once() + + self.assertEqual(Document.objects.filter(owner=self.owner).count(), 3) + self.assertEqual(Document.objects.filter(id__in=doc_ids).count(), 3) + + users_with_perms = get_users_with_perms( + self.doc1, + ) + self.assertEqual(users_with_perms.count(), 2) + + # group1 should be replaced by group2 + groups_with_perms = get_groups_with_perms( + self.doc1, + ) + self.assertEqual(groups_with_perms.count(), 1) + + @mock.patch("documents.tasks.bulk_update_documents.delay") + def test_set_permissions_merge(self, m): + doc_ids = [self.doc1.id, self.doc2.id, self.doc3.id] + + self.doc1.owner = self.user1 + self.doc1.save() + + assign_perm("view_document", self.user1, self.doc1) + assign_perm("view_document", self.group1, self.doc1) + + permissions = { + "view": { + "users": [self.user2.id], + "groups": [self.group2.id], + }, + "change": { + "users": [self.user2.id], + "groups": [self.group2.id], + }, + } + set_permissions( + doc_ids, + set_permissions=permissions, + owner=self.owner, + merge=True, + ) + m.assert_called_once() + + # when merge is true owner doesn't get replaced if its not empty + self.assertEqual(Document.objects.filter(owner=self.owner).count(), 2) + self.assertEqual(Document.objects.filter(id__in=doc_ids).count(), 3) + + # merge of user1 which was pre-existing and user2 + users_with_perms = get_users_with_perms( + self.doc1, + ) + self.assertEqual(users_with_perms.count(), 2) + + # group1 should be merged by group2 + groups_with_perms = get_groups_with_perms( + self.doc1, + ) + self.assertEqual(groups_with_perms.count(), 2) diff --git a/src/documents/tests/test_api_bulk_edit.py b/src/documents/tests/test_api_bulk_edit.py index c2dc69a1e..4cf9909c0 100644 --- a/src/documents/tests/test_api_bulk_edit.py +++ b/src/documents/tests/test_api_bulk_edit.py @@ -765,6 +765,58 @@ class TestBulkEdit(DirectoriesMixin, APITestCase): self.assertCountEqual(args[0], [self.doc2.id, self.doc3.id]) self.assertEqual(len(kwargs["set_permissions"]["view"]["users"]), 2) + @mock.patch("documents.serialisers.bulk_edit.set_permissions") + def test_set_permissions_merge(self, m): + m.return_value = "OK" + user1 = User.objects.create(username="user1") + user2 = User.objects.create(username="user2") + permissions = { + "view": { + "users": [user1.id, user2.id], + "groups": None, + }, + "change": { + "users": [user1.id], + "groups": None, + }, + } + + response = self.client.post( + "/api/documents/bulk_edit/", + json.dumps( + { + "documents": [self.doc2.id, self.doc3.id], + "method": "set_permissions", + "parameters": {"set_permissions": permissions}, + }, + ), + content_type="application/json", + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + m.assert_called() + args, kwargs = m.call_args + self.assertEqual(kwargs["merge"], False) + + response = self.client.post( + "/api/documents/bulk_edit/", + json.dumps( + { + "documents": [self.doc2.id, self.doc3.id], + "method": "set_permissions", + "parameters": {"set_permissions": permissions, "merge": True}, + }, + ), + content_type="application/json", + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + m.assert_called() + args, kwargs = m.call_args + self.assertEqual(kwargs["merge"], True) + @mock.patch("documents.serialisers.bulk_edit.set_permissions") def test_insufficient_permissions_ownership(self, m): """ diff --git a/src/documents/tests/test_api_permissions.py b/src/documents/tests/test_api_permissions.py index a0b22586b..851608f37 100644 --- a/src/documents/tests/test_api_permissions.py +++ b/src/documents/tests/test_api_permissions.py @@ -700,8 +700,8 @@ class TestBulkEditObjectPermissions(APITestCase): def setUp(self): super().setUp() - user = User.objects.create_superuser(username="temp_admin") - self.client.force_authenticate(user=user) + self.temp_admin = User.objects.create_superuser(username="temp_admin") + self.client.force_authenticate(user=self.temp_admin) self.t1 = Tag.objects.create(name="t1") self.t2 = Tag.objects.create(name="t2") @@ -822,6 +822,79 @@ class TestBulkEditObjectPermissions(APITestCase): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(StoragePath.objects.get(pk=self.sp1.id).owner, self.user3) + def test_bulk_object_set_permissions_merge(self): + """ + GIVEN: + - Existing objects + WHEN: + - bulk_edit_object_perms API endpoint is called with merge=True or merge=False (default) + THEN: + - Permissions and / or owner are replaced or merged, depending on the merge flag + """ + permissions = { + "view": { + "users": [self.user1.id, self.user2.id], + "groups": [], + }, + "change": { + "users": [self.user1.id], + "groups": [], + }, + } + + assign_perm("view_tag", self.user3, self.t1) + self.t1.owner = self.user3 + self.t1.save() + + # merge=True + response = self.client.post( + "/api/bulk_edit_object_perms/", + json.dumps( + { + "objects": [self.t1.id, self.t2.id], + "object_type": "tags", + "owner": self.user1.id, + "permissions": permissions, + "merge": True, + }, + ), + content_type="application/json", + ) + + self.t1.refresh_from_db() + self.t2.refresh_from_db() + + self.assertEqual(response.status_code, status.HTTP_200_OK) + # user3 should still be owner of t1 since was set prior + self.assertEqual(self.t1.owner, self.user3) + # user1 should now be owner of t2 since it didn't have an owner + self.assertEqual(self.t2.owner, self.user1) + + # user1 should be added + self.assertIn(self.user1, get_users_with_perms(self.t1)) + # user3 should be preserved + self.assertIn(self.user3, get_users_with_perms(self.t1)) + + # merge=False (default) + response = self.client.post( + "/api/bulk_edit_object_perms/", + json.dumps( + { + "objects": [self.t1.id, self.t2.id], + "object_type": "tags", + "permissions": permissions, + "merge": False, + }, + ), + content_type="application/json", + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + # user1 should be added + self.assertIn(self.user1, get_users_with_perms(self.t1)) + # user3 should be removed + self.assertNotIn(self.user3, get_users_with_perms(self.t1)) + def test_bulk_edit_object_permissions_insufficient_perms(self): """ GIVEN: diff --git a/src/documents/views.py b/src/documents/views.py index 9d44ecbc4..11fb5b1f2 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -1385,6 +1385,7 @@ class BulkEditObjectPermissionsView(GenericAPIView, PassUserMixin): object_class = serializer.get_object_class(object_type) permissions = serializer.validated_data.get("permissions") owner = serializer.validated_data.get("owner") + merge = serializer.validated_data.get("merge") if not user.is_superuser: objs = object_class.objects.filter(pk__in=object_ids) @@ -1396,12 +1397,21 @@ class BulkEditObjectPermissionsView(GenericAPIView, PassUserMixin): try: qs = object_class.objects.filter(id__in=object_ids) - if "owner" in serializer.validated_data: - qs.update(owner=owner) + # if merge is true, we dont want to remove the owner + if "owner" in serializer.validated_data and ( + not merge or (merge and owner is not None) + ): + # if merge is true, we dont want to overwrite the owner + qs_owner_update = qs.filter(owner__isnull=True) if merge else qs + qs_owner_update.update(owner=owner) if "permissions" in serializer.validated_data: for obj in qs: - set_permissions_for_object(permissions, obj) + set_permissions_for_object( + permissions=permissions, + object=obj, + merge=merge, + ) return Response({"result": "OK"}) except Exception as e: From 2aced1c305786be6781238ec6bd19ca64c1cf129 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 12:29:38 -0800 Subject: [PATCH 10/41] Chore(deps): Bump the actions group with 1 update (#5629) Bumps the actions group with 1 update: [codecov/codecov-action](https://github.com/codecov/codecov-action). Updates `codecov/codecov-action` from 3 to 4 - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v4) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ec8407611..6e19d03a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -283,7 +283,7 @@ jobs: merge-multiple: true - name: Upload frontend coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: # not required for public repos, but intermittently fails otherwise token: ${{ secrets.CODECOV_TOKEN }} @@ -299,7 +299,7 @@ jobs: path: src/ - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: # not required for public repos, but intermittently fails otherwise token: ${{ secrets.CODECOV_TOKEN }} From 9d84e957714fd11e8335737650e2c80c6f943ad8 Mon Sep 17 00:00:00 2001 From: Henrik Gerdes Date: Thu, 1 Feb 2024 21:42:23 +0100 Subject: [PATCH 11/41] feat: add env allowing paperless to run in read_only filesystem (#5596) --- docker/paperless_cmd.sh | 5 +++-- docs/configuration.md | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/docker/paperless_cmd.sh b/docker/paperless_cmd.sh index 81a7c6334..afedb1599 100755 --- a/docker/paperless_cmd.sh +++ b/docker/paperless_cmd.sh @@ -1,14 +1,15 @@ #!/usr/bin/env bash +SUPERVISORD_WORKING_DIR="${PAPERLESS_SUPERVISORD_WORKING_DIR:-$PWD}" rootless_args=() if [ "$(id -u)" == "$(id -u paperless)" ]; then rootless_args=( --user paperless --logfile - supervisord.log + "${SUPERVISORD_WORKING_DIR}/supervisord.log" --pidfile - supervisord.pid + "${SUPERVISORD_WORKING_DIR}/supervisord.pid" ) fi diff --git a/docs/configuration.md b/docs/configuration.md index be363c269..6dc341346 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1341,6 +1341,12 @@ started by the container. You can read more about this in the [advanced documentation](advanced_usage.md#celery-monitoring). +#### [`PAPERLESS_SUPERVISORD_WORKING_DIR=`](#PAPERLESS_SUPERVISORD_WORKING_DIR) {#PAPERLESS_SUPERVISORD_WORKING_DIR} + +: If this environment variable is defined, the `supervisord.log` and `supervisord.pid` file will be created under the specified path in `PAPERLESS_SUPERVISORD_WORKING_DIR`. Setting `PAPERLESS_SUPERVISORD_WORKING_DIR=/tmp` and `PYTHONPYCACHEPREFIX=/tmp/pycache` would allow paperless to work on a read-only filesystem. + + Please take note that the `PAPERLESS_DATA_DIR` and `PAPERLESS_MEDIA_ROOT` paths still have to be writable, just like the `PAPERLESS_SUPERVISORD_WORKING_DIR`. The can be archived by using bind or volume mounts. Only works in the container is run as user *paperless* + ## Frontend Settings #### [`PAPERLESS_APP_TITLE=`](#PAPERLESS_APP_TITLE) {#PAPERLESS_APP_TITLE} From 11e9c4d8cc5b352e343fc453241165bf22a25e18 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 21:18:29 +0000 Subject: [PATCH 12/41] Chore(deps-dev): Bump @types/node from 20.10.6 to 20.11.16 in /src-ui (#5635) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.10.6 to 20.11.16. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src-ui/package-lock.json | 8 ++++---- src-ui/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src-ui/package-lock.json b/src-ui/package-lock.json index f51e4056e..76a311c13 100644 --- a/src-ui/package-lock.json +++ b/src-ui/package-lock.json @@ -48,7 +48,7 @@ "@angular/compiler-cli": "~17.0.7", "@playwright/test": "^1.40.1", "@types/jest": "^29.5.10", - "@types/node": "^20.10.6", + "@types/node": "^20.11.16", "@typescript-eslint/eslint-plugin": "^6.17.0", "@typescript-eslint/parser": "^6.17.0", "concurrently": "^8.2.2", @@ -4879,9 +4879,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.10.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.6.tgz", - "integrity": "sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==", + "version": "20.11.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.16.tgz", + "integrity": "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ==", "dev": true, "dependencies": { "undici-types": "~5.26.4" diff --git a/src-ui/package.json b/src-ui/package.json index baefa35c3..0418f9f48 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -50,7 +50,7 @@ "@angular/compiler-cli": "~17.0.7", "@playwright/test": "^1.40.1", "@types/jest": "^29.5.10", - "@types/node": "^20.10.6", + "@types/node": "^20.11.16", "@typescript-eslint/eslint-plugin": "^6.17.0", "@typescript-eslint/parser": "^6.17.0", "concurrently": "^8.2.2", From 89aff63e522ec9435bc6dadeeff64e4d02a6fe90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 21:26:21 +0000 Subject: [PATCH 13/41] Chore(deps): Bump zone.js from 0.14.2 to 0.14.3 in /src-ui (#5633) Bumps [zone.js](https://github.com/angular/angular/tree/HEAD/packages/zone.js) from 0.14.2 to 0.14.3. - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/packages/zone.js/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/zone.js-0.14.3/packages/zone.js) --- updated-dependencies: - dependency-name: zone.js dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src-ui/package-lock.json | 8 ++++---- src-ui/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src-ui/package-lock.json b/src-ui/package-lock.json index 76a311c13..684972d22 100644 --- a/src-ui/package-lock.json +++ b/src-ui/package-lock.json @@ -34,7 +34,7 @@ "rxjs": "^7.8.1", "tslib": "^2.6.2", "uuid": "^9.0.1", - "zone.js": "^0.14.2" + "zone.js": "^0.14.3" }, "devDependencies": { "@angular-builders/jest": "17.0.0", @@ -19356,9 +19356,9 @@ } }, "node_modules/zone.js": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.2.tgz", - "integrity": "sha512-X4U7J1isDhoOmHmFWiLhloWc2lzMkdnumtfQ1LXzf/IOZp5NQYuMUTaviVzG/q1ugMBIXzin2AqeVJUoSEkNyQ==", + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.3.tgz", + "integrity": "sha512-jYoNqF046Q+JfcZSItRSt+oXFcpXL88yq7XAZjb/NKTS7w2hHpKjRJ3VlFD1k75wMaRRXNUt5vrZVlygiMyHbA==", "dependencies": { "tslib": "^2.3.0" } diff --git a/src-ui/package.json b/src-ui/package.json index 0418f9f48..3cf50b3b3 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -36,7 +36,7 @@ "rxjs": "^7.8.1", "tslib": "^2.6.2", "uuid": "^9.0.1", - "zone.js": "^0.14.2" + "zone.js": "^0.14.3" }, "devDependencies": { "@angular-builders/jest": "17.0.0", From d1b516a089bb1f7500dda48e77e7020185a3990f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 21:36:09 +0000 Subject: [PATCH 14/41] Chore(deps-dev): Bump the frontend-eslint-dependencies group (#5632) Bumps the frontend-eslint-dependencies group in /src-ui with 2 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser). Updates `@typescript-eslint/eslint-plugin` from 6.17.0 to 6.20.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.20.0/packages/eslint-plugin) Updates `@typescript-eslint/parser` from 6.17.0 to 6.20.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.20.0/packages/parser) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: frontend-eslint-dependencies - dependency-name: "@typescript-eslint/parser" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: frontend-eslint-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src-ui/package-lock.json | 88 ++++++++++++++++++++-------------------- src-ui/package.json | 4 +- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src-ui/package-lock.json b/src-ui/package-lock.json index 684972d22..c974120ba 100644 --- a/src-ui/package-lock.json +++ b/src-ui/package-lock.json @@ -49,8 +49,8 @@ "@playwright/test": "^1.40.1", "@types/jest": "^29.5.10", "@types/node": "^20.11.16", - "@typescript-eslint/eslint-plugin": "^6.17.0", - "@typescript-eslint/parser": "^6.17.0", + "@typescript-eslint/eslint-plugin": "^6.20.0", + "@typescript-eslint/parser": "^6.20.0", "concurrently": "^8.2.2", "eslint": "^8.56.0", "jest": "29.7.0", @@ -4996,16 +4996,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.17.0.tgz", - "integrity": "sha512-Vih/4xLXmY7V490dGwBQJTpIZxH4ZFH6eCVmQ4RFkB+wmaCTDAx4dtgoWwMNGKLkqRY1L6rPqzEbjorRnDo4rQ==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.20.0.tgz", + "integrity": "sha512-fTwGQUnjhoYHeSF6m5pWNkzmDDdsKELYrOBxhjMrofPqCkoC2k3B2wvGHFxa1CTIqkEn88nlW1HVMztjo2K8Hg==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.17.0", - "@typescript-eslint/type-utils": "6.17.0", - "@typescript-eslint/utils": "6.17.0", - "@typescript-eslint/visitor-keys": "6.17.0", + "@typescript-eslint/scope-manager": "6.20.0", + "@typescript-eslint/type-utils": "6.20.0", + "@typescript-eslint/utils": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -5031,13 +5031,13 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.17.0.tgz", - "integrity": "sha512-hDXcWmnbtn4P2B37ka3nil3yi3VCQO2QEB9gBiHJmQp5wmyQWqnjA85+ZcE8c4FqnaB6lBwMrPkgd4aBYz3iNg==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.20.0.tgz", + "integrity": "sha512-qnSobiJQb1F5JjN0YDRPHruQTrX7ICsmltXhkV536mp4idGAYrIyr47zF/JmkJtEcAVnIz4gUYJ7gOZa6SmN4g==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.17.0", - "@typescript-eslint/utils": "6.17.0", + "@typescript-eslint/typescript-estree": "6.20.0", + "@typescript-eslint/utils": "6.20.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -5058,17 +5058,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.17.0.tgz", - "integrity": "sha512-LofsSPjN/ITNkzV47hxas2JCsNCEnGhVvocfyOcLzT9c/tSZE7SfhS/iWtzP1lKNOEfLhRTZz6xqI8N2RzweSQ==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.20.0.tgz", + "integrity": "sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.17.0", - "@typescript-eslint/types": "6.17.0", - "@typescript-eslint/typescript-estree": "6.17.0", + "@typescript-eslint/scope-manager": "6.20.0", + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/typescript-estree": "6.20.0", "semver": "^7.5.4" }, "engines": { @@ -5083,15 +5083,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.17.0.tgz", - "integrity": "sha512-C4bBaX2orvhK+LlwrY8oWGmSl4WolCfYm513gEccdWZj0CwGadbIADb0FtVEcI+WzUyjyoBj2JRP8g25E6IB8A==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.20.0.tgz", + "integrity": "sha512-bYerPDF/H5v6V76MdMYhjwmwgMA+jlPVqjSDq2cRqMi8bP5sR3Z+RLOiOMad3nsnmDVmn2gAFCyNgh/dIrfP/w==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.17.0", - "@typescript-eslint/types": "6.17.0", - "@typescript-eslint/typescript-estree": "6.17.0", - "@typescript-eslint/visitor-keys": "6.17.0", + "@typescript-eslint/scope-manager": "6.20.0", + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/typescript-estree": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", "debug": "^4.3.4" }, "engines": { @@ -5111,13 +5111,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.17.0.tgz", - "integrity": "sha512-RX7a8lwgOi7am0k17NUO0+ZmMOX4PpjLtLRgLmT1d3lBYdWH4ssBUbwdmc5pdRX8rXon8v9x8vaoOSpkHfcXGA==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz", + "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.17.0", - "@typescript-eslint/visitor-keys": "6.17.0" + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -5212,9 +5212,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.17.0.tgz", - "integrity": "sha512-qRKs9tvc3a4RBcL/9PXtKSehI/q8wuU9xYJxe97WFxnzH8NWWtcW3ffNS+EWg8uPvIerhjsEZ+rHtDqOCiH57A==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", + "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -5225,13 +5225,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.17.0.tgz", - "integrity": "sha512-gVQe+SLdNPfjlJn5VNGhlOhrXz4cajwFd5kAgWtZ9dCZf4XJf8xmgCTLIqec7aha3JwgLI2CK6GY1043FRxZwg==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", + "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.17.0", - "@typescript-eslint/visitor-keys": "6.17.0", + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -5376,12 +5376,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.17.0.tgz", - "integrity": "sha512-H6VwB/k3IuIeQOyYczyyKN8wH6ed8EwliaYHLxOIhyF0dYEIsN8+Bk3GE19qafeMKyZJJHP8+O1HiFhFLUNKSg==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", + "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.17.0", + "@typescript-eslint/types": "6.20.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { diff --git a/src-ui/package.json b/src-ui/package.json index 3cf50b3b3..656119f0e 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -51,8 +51,8 @@ "@playwright/test": "^1.40.1", "@types/jest": "^29.5.10", "@types/node": "^20.11.16", - "@typescript-eslint/eslint-plugin": "^6.17.0", - "@typescript-eslint/parser": "^6.17.0", + "@typescript-eslint/eslint-plugin": "^6.20.0", + "@typescript-eslint/parser": "^6.20.0", "concurrently": "^8.2.2", "eslint": "^8.56.0", "jest": "29.7.0", From 6090305b77850202f213148489b4ec47c6aeacaf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 21:43:36 +0000 Subject: [PATCH 15/41] Chore(deps-dev): Bump the frontend-jest-dependencies group (#5631) Bumps the frontend-jest-dependencies group in /src-ui with 2 updates: [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) and [jest-preset-angular](https://github.com/thymikee/jest-preset-angular). Updates `@types/jest` from 29.5.11 to 29.5.12 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest) Updates `jest-preset-angular` from 13.1.4 to 14.0.0 - [Release notes](https://github.com/thymikee/jest-preset-angular/releases) - [Changelog](https://github.com/thymikee/jest-preset-angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/thymikee/jest-preset-angular/compare/v13.1.4...v14.0.0) --- updated-dependencies: - dependency-name: "@types/jest" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: frontend-jest-dependencies - dependency-name: jest-preset-angular dependency-type: direct:development update-type: version-update:semver-major dependency-group: frontend-jest-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src-ui/package-lock.json | 52 ++++++++++++++++++++++++++++++---------- src-ui/package.json | 4 ++-- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src-ui/package-lock.json b/src-ui/package-lock.json index c974120ba..ef002b1e9 100644 --- a/src-ui/package-lock.json +++ b/src-ui/package-lock.json @@ -47,7 +47,7 @@ "@angular/cli": "~17.0.8", "@angular/compiler-cli": "~17.0.7", "@playwright/test": "^1.40.1", - "@types/jest": "^29.5.10", + "@types/jest": "^29.5.12", "@types/node": "^20.11.16", "@typescript-eslint/eslint-plugin": "^6.20.0", "@typescript-eslint/parser": "^6.20.0", @@ -55,7 +55,7 @@ "eslint": "^8.56.0", "jest": "29.7.0", "jest-environment-jsdom": "^29.7.0", - "jest-preset-angular": "^13.1.4", + "jest-preset-angular": "^14.0.0", "jest-websocket-mock": "^2.5.0", "patch-package": "^8.0.0", "ts-node": "~10.9.1", @@ -107,6 +107,34 @@ "jest": ">=29" } }, + "node_modules/@angular-builders/jest/node_modules/jest-preset-angular": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-13.1.4.tgz", + "integrity": "sha512-XKeWa8Qt7p37SzlJ85qEXgig06SgkfrzV057X2GSMqfz/HLJmTUjMFkHJKe65ZaQumNQWCcXpxXREr6EfZ9bow==", + "dev": true, + "dependencies": { + "bs-logger": "^0.2.6", + "esbuild-wasm": ">=0.13.8", + "jest-environment-jsdom": "^29.0.0", + "jest-util": "^29.0.0", + "pretty-format": "^29.0.0", + "ts-jest": "^29.0.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "optionalDependencies": { + "esbuild": ">=0.13.8" + }, + "peerDependencies": { + "@angular-devkit/build-angular": ">=13.0.0 <18.0.0", + "@angular/compiler-cli": ">=13.0.0 <18.0.0", + "@angular/core": ">=13.0.0 <18.0.0", + "@angular/platform-browser-dynamic": ">=13.0.0 <18.0.0", + "jest": "^29.0.0", + "typescript": ">=4.4" + } + }, "node_modules/@angular-devkit/architect": { "version": "0.1700.8", "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1700.8.tgz", @@ -4846,9 +4874,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.11", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", - "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", + "version": "29.5.12", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", + "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -11779,9 +11807,9 @@ } }, "node_modules/jest-preset-angular": { - "version": "13.1.4", - "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-13.1.4.tgz", - "integrity": "sha512-XKeWa8Qt7p37SzlJ85qEXgig06SgkfrzV057X2GSMqfz/HLJmTUjMFkHJKe65ZaQumNQWCcXpxXREr6EfZ9bow==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-14.0.0.tgz", + "integrity": "sha512-gXGgzuGbpw3MRBMe/NGCu3r2E//GKmhtFveo0XUIXMvQ3je0vcOtK+WYjxtxFTTh2xFgrA/loY5BxBcKia/GaA==", "dev": true, "dependencies": { "bs-logger": "^0.2.6", @@ -11798,10 +11826,10 @@ "esbuild": ">=0.13.8" }, "peerDependencies": { - "@angular-devkit/build-angular": ">=13.0.0 <18.0.0", - "@angular/compiler-cli": ">=13.0.0 <18.0.0", - "@angular/core": ">=13.0.0 <18.0.0", - "@angular/platform-browser-dynamic": ">=13.0.0 <18.0.0", + "@angular-devkit/build-angular": ">=15.0.0 <18.0.0", + "@angular/compiler-cli": ">=15.0.0 <18.0.0", + "@angular/core": ">=15.0.0 <18.0.0", + "@angular/platform-browser-dynamic": ">=15.0.0 <18.0.0", "jest": "^29.0.0", "typescript": ">=4.4" } diff --git a/src-ui/package.json b/src-ui/package.json index 656119f0e..620bef76f 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -49,7 +49,7 @@ "@angular/cli": "~17.0.8", "@angular/compiler-cli": "~17.0.7", "@playwright/test": "^1.40.1", - "@types/jest": "^29.5.10", + "@types/jest": "^29.5.12", "@types/node": "^20.11.16", "@typescript-eslint/eslint-plugin": "^6.20.0", "@typescript-eslint/parser": "^6.20.0", @@ -57,7 +57,7 @@ "eslint": "^8.56.0", "jest": "29.7.0", "jest-environment-jsdom": "^29.7.0", - "jest-preset-angular": "^13.1.4", + "jest-preset-angular": "^14.0.0", "jest-websocket-mock": "^2.5.0", "patch-package": "^8.0.0", "ts-node": "~10.9.1", From 9e6aa552302de4df4737092f25b4ff04c605832a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 14:49:49 -0800 Subject: [PATCH 16/41] Chore(deps): Bump the frontend-angular-dependencies group (#5630) Bumps the frontend-angular-dependencies group in /src-ui with 19 updates: | Package | From | To | | --- | --- | --- | | [@angular/cdk](https://github.com/angular/components) | `17.0.4` | `17.1.2` | | [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) | `17.0.8` | `17.1.2` | | [@angular/compiler](https://github.com/angular/angular/tree/HEAD/packages/compiler) | `17.0.8` | `17.1.2` | | [@angular/core](https://github.com/angular/angular/tree/HEAD/packages/core) | `17.0.8` | `17.1.2` | | [@angular/forms](https://github.com/angular/angular/tree/HEAD/packages/forms) | `17.0.8` | `17.1.2` | | [@angular/localize](https://github.com/angular/angular) | `17.0.8` | `17.1.2` | | [@angular/platform-browser](https://github.com/angular/angular/tree/HEAD/packages/platform-browser) | `17.0.8` | `17.1.2` | | [@angular/platform-browser-dynamic](https://github.com/angular/angular/tree/HEAD/packages/platform-browser-dynamic) | `17.0.8` | `17.1.2` | | [@angular/router](https://github.com/angular/angular/tree/HEAD/packages/router) | `17.0.8` | `17.1.2` | | [@ng-select/ng-select](https://github.com/ng-select/ng-select) | `12.0.4` | `12.0.6` | | [ngx-ui-tour-ng-bootstrap](https://github.com/hakimio/ngx-ui-tour) | `14.0.1` | `14.0.2` | | [@angular-devkit/build-angular](https://github.com/angular/angular-cli) | `17.0.8` | `17.1.2` | | [@angular-eslint/builder](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/builder) | `17.1.1` | `17.2.1` | | [@angular-eslint/eslint-plugin](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/eslint-plugin) | `17.1.1` | `17.2.1` | | [@angular-eslint/eslint-plugin-template](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/eslint-plugin-template) | `17.1.1` | `17.2.1` | | [@angular-eslint/schematics](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/schematics) | `17.1.1` | `17.2.1` | | [@angular-eslint/template-parser](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/template-parser) | `17.1.1` | `17.2.1` | | [@angular/cli](https://github.com/angular/angular-cli) | `17.0.8` | `17.1.2` | | [@angular/compiler-cli](https://github.com/angular/angular/tree/HEAD/packages/compiler-cli) | `17.0.8` | `17.1.2` | Updates `@angular/cdk` from 17.0.4 to 17.1.2 - [Release notes](https://github.com/angular/components/releases) - [Changelog](https://github.com/angular/components/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/components/compare/17.0.4...17.1.2) Updates `@angular/common` from 17.0.8 to 17.1.2 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/17.1.2/packages/common) Updates `@angular/compiler` from 17.0.8 to 17.1.2 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/17.1.2/packages/compiler) Updates `@angular/core` from 17.0.8 to 17.1.2 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/17.1.2/packages/core) Updates `@angular/forms` from 17.0.8 to 17.1.2 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/17.1.2/packages/forms) Updates `@angular/localize` from 17.0.8 to 17.1.2 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/compare/17.0.8...17.1.2) Updates `@angular/platform-browser` from 17.0.8 to 17.1.2 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/17.1.2/packages/platform-browser) Updates `@angular/platform-browser-dynamic` from 17.0.8 to 17.1.2 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/17.1.2/packages/platform-browser-dynamic) Updates `@angular/router` from 17.0.8 to 17.1.2 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/17.1.2/packages/router) Updates `@ng-select/ng-select` from 12.0.4 to 12.0.6 - [Release notes](https://github.com/ng-select/ng-select/releases) - [Changelog](https://github.com/ng-select/ng-select/blob/master/CHANGELOG.md) - [Commits](https://github.com/ng-select/ng-select/compare/v12.0.4...v12.0.6) Updates `ngx-ui-tour-ng-bootstrap` from 14.0.1 to 14.0.2 - [Release notes](https://github.com/hakimio/ngx-ui-tour/releases) - [Commits](https://github.com/hakimio/ngx-ui-tour/commits) Updates `@angular-devkit/build-angular` from 17.0.8 to 17.1.2 - [Release notes](https://github.com/angular/angular-cli/releases) - [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular-cli/compare/17.0.8...17.1.2) Updates `@angular-eslint/builder` from 17.1.1 to 17.2.1 - [Release notes](https://github.com/angular-eslint/angular-eslint/releases) - [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/builder/CHANGELOG.md) - [Commits](https://github.com/angular-eslint/angular-eslint/commits/v17.2.1/packages/builder) Updates `@angular-eslint/eslint-plugin` from 17.1.1 to 17.2.1 - [Release notes](https://github.com/angular-eslint/angular-eslint/releases) - [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/angular-eslint/angular-eslint/commits/v17.2.1/packages/eslint-plugin) Updates `@angular-eslint/eslint-plugin-template` from 17.1.1 to 17.2.1 - [Release notes](https://github.com/angular-eslint/angular-eslint/releases) - [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin-template/CHANGELOG.md) - [Commits](https://github.com/angular-eslint/angular-eslint/commits/v17.2.1/packages/eslint-plugin-template) Updates `@angular-eslint/schematics` from 17.1.1 to 17.2.1 - [Release notes](https://github.com/angular-eslint/angular-eslint/releases) - [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/schematics/CHANGELOG.md) - [Commits](https://github.com/angular-eslint/angular-eslint/commits/v17.2.1/packages/schematics) Updates `@angular-eslint/template-parser` from 17.1.1 to 17.2.1 - [Release notes](https://github.com/angular-eslint/angular-eslint/releases) - [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/template-parser/CHANGELOG.md) - [Commits](https://github.com/angular-eslint/angular-eslint/commits/v17.2.1/packages/template-parser) Updates `@angular/cli` from 17.0.8 to 17.1.2 - [Release notes](https://github.com/angular/angular-cli/releases) - [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular-cli/compare/17.0.8...17.1.2) Updates `@angular/compiler-cli` from 17.0.8 to 17.1.2 - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/17.1.2/packages/compiler-cli) --- updated-dependencies: - dependency-name: "@angular/cdk" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular/common" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular/compiler" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular/core" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular/forms" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular/localize" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular/platform-browser" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular/platform-browser-dynamic" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular/router" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@ng-select/ng-select" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: frontend-angular-dependencies - dependency-name: ngx-ui-tour-ng-bootstrap dependency-type: direct:production update-type: version-update:semver-patch dependency-group: frontend-angular-dependencies - dependency-name: "@angular-devkit/build-angular" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular-eslint/builder" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular-eslint/eslint-plugin-template" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular-eslint/schematics" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular-eslint/template-parser" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular/cli" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies - dependency-name: "@angular/compiler-cli" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src-ui/package-lock.json | 3854 +++++++++++++++----------------------- src-ui/package.json | 38 +- 2 files changed, 1494 insertions(+), 2398 deletions(-) diff --git a/src-ui/package-lock.json b/src-ui/package-lock.json index ef002b1e9..1076db189 100644 --- a/src-ui/package-lock.json +++ b/src-ui/package-lock.json @@ -9,17 +9,17 @@ "version": "0.0.0", "hasInstallScript": true, "dependencies": { - "@angular/cdk": "^17.0.4", - "@angular/common": "~17.0.8", - "@angular/compiler": "~17.0.8", - "@angular/core": "~17.0.8", - "@angular/forms": "~17.0.8", - "@angular/localize": "~17.0.8", - "@angular/platform-browser": "~17.0.8", - "@angular/platform-browser-dynamic": "~17.0.8", - "@angular/router": "~17.0.8", + "@angular/cdk": "^17.1.2", + "@angular/common": "~17.1.2", + "@angular/compiler": "~17.1.2", + "@angular/core": "~17.1.2", + "@angular/forms": "~17.1.2", + "@angular/localize": "~17.1.2", + "@angular/platform-browser": "~17.1.2", + "@angular/platform-browser-dynamic": "~17.1.2", + "@angular/router": "~17.1.2", "@ng-bootstrap/ng-bootstrap": "^16.0.0", - "@ng-select/ng-select": "^12.0.4", + "@ng-select/ng-select": "^12.0.6", "@ngneat/dirty-check-forms": "^3.0.3", "@popperjs/core": "^2.11.8", "bootstrap": "^5.3.2", @@ -29,7 +29,7 @@ "ngx-color": "^9.0.0", "ngx-cookie-service": "^17.0.1", "ngx-file-drop": "^16.0.0", - "ngx-ui-tour-ng-bootstrap": "^14.0.1", + "ngx-ui-tour-ng-bootstrap": "^14.0.2", "pdfjs-dist": "^3.11.174", "rxjs": "^7.8.1", "tslib": "^2.6.2", @@ -38,14 +38,14 @@ }, "devDependencies": { "@angular-builders/jest": "17.0.0", - "@angular-devkit/build-angular": "~17.0.8", - "@angular-eslint/builder": "17.1.1", - "@angular-eslint/eslint-plugin": "17.1.1", - "@angular-eslint/eslint-plugin-template": "17.1.1", - "@angular-eslint/schematics": "17.1.1", - "@angular-eslint/template-parser": "17.1.1", - "@angular/cli": "~17.0.8", - "@angular/compiler-cli": "~17.0.7", + "@angular-devkit/build-angular": "~17.1.2", + "@angular-eslint/builder": "17.2.1", + "@angular-eslint/eslint-plugin": "17.2.1", + "@angular-eslint/eslint-plugin-template": "17.2.1", + "@angular-eslint/schematics": "17.2.1", + "@angular-eslint/template-parser": "17.2.1", + "@angular/cli": "~17.1.2", + "@angular/compiler-cli": "~17.1.2", "@playwright/test": "^1.40.1", "@types/jest": "^29.5.12", "@types/node": "^20.11.16", @@ -136,12 +136,12 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1700.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1700.8.tgz", - "integrity": "sha512-SWVr3CvwO6T0yW2ytszCwBT1g92vyFkwbVUxqE93urYnoD8PvP+81GH5YwVjHQTgvhP4eXQMGZ9hpHx57VOrWQ==", + "version": "0.1701.2", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1701.2.tgz", + "integrity": "sha512-g3gn5Ht6r9bCeFeAYF+HboZB8IvgvqqdeOnaWNaXJLI0ymEkpbqRdqrHGuVKHJV7JOMNXC7GPJEctBC6SXxOxA==", "dev": true, "dependencies": { - "@angular-devkit/core": "17.0.8", + "@angular-devkit/core": "17.1.2", "rxjs": "7.8.1" }, "engines": { @@ -151,42 +151,40 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-17.0.8.tgz", - "integrity": "sha512-u7R5yX92ZxOL/LfxiKGGqlBo86100sJ5Rabavn8DeGtYP8N0qgwCcNwlW2zaMoUlkw2geMnxcxIX5VJI4iFPUA==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-17.1.2.tgz", + "integrity": "sha512-QIDTP+TjiCKCYRZYb8to4ymvIV1Djcfd5c17VdgMGhRqIQAAK1V4f4A1njdhGYOrgsLajZQAnKvFfk2ZMeI37A==", "dev": true, "dependencies": { "@ampproject/remapping": "2.2.1", - "@angular-devkit/architect": "0.1700.8", - "@angular-devkit/build-webpack": "0.1700.8", - "@angular-devkit/core": "17.0.8", - "@babel/core": "7.23.2", - "@babel/generator": "7.23.0", + "@angular-devkit/architect": "0.1701.2", + "@angular-devkit/build-webpack": "0.1701.2", + "@angular-devkit/core": "17.1.2", + "@babel/core": "7.23.7", + "@babel/generator": "7.23.6", "@babel/helper-annotate-as-pure": "7.22.5", "@babel/helper-split-export-declaration": "7.22.6", - "@babel/plugin-transform-async-generator-functions": "7.23.2", - "@babel/plugin-transform-async-to-generator": "7.22.5", - "@babel/plugin-transform-runtime": "7.23.2", - "@babel/preset-env": "7.23.2", - "@babel/runtime": "7.23.2", + "@babel/plugin-transform-async-generator-functions": "7.23.7", + "@babel/plugin-transform-async-to-generator": "7.23.3", + "@babel/plugin-transform-runtime": "7.23.7", + "@babel/preset-env": "7.23.7", + "@babel/runtime": "7.23.7", "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "17.0.8", - "@vitejs/plugin-basic-ssl": "1.0.1", + "@ngtools/webpack": "17.1.2", + "@vitejs/plugin-basic-ssl": "1.0.2", "ansi-colors": "4.1.3", "autoprefixer": "10.4.16", "babel-loader": "9.1.3", "babel-plugin-istanbul": "6.1.1", - "browser-sync": "2.29.3", "browserslist": "^4.21.5", - "chokidar": "3.5.3", "copy-webpack-plugin": "11.0.0", "critters": "0.0.20", "css-loader": "6.8.1", - "esbuild-wasm": "0.19.5", - "fast-glob": "3.3.1", + "esbuild-wasm": "0.19.11", + "fast-glob": "3.3.2", "http-proxy-middleware": "2.0.6", "https-proxy-agent": "7.0.2", - "inquirer": "9.2.11", + "inquirer": "9.2.12", "jsonc-parser": "3.2.0", "karma-source-map-support": "1.4.0", "less": "4.2.0", @@ -195,27 +193,28 @@ "loader-utils": "3.2.1", "magic-string": "0.30.5", "mini-css-extract-plugin": "2.7.6", - "mrmime": "1.0.1", + "mrmime": "2.0.0", "open": "8.4.2", "ora": "5.4.1", "parse5-html-rewriting-stream": "7.0.0", "picomatch": "3.0.1", - "piscina": "4.1.0", - "postcss": "8.4.31", - "postcss-loader": "7.3.3", + "piscina": "4.2.1", + "postcss": "8.4.33", + "postcss-loader": "7.3.4", "resolve-url-loader": "5.0.0", "rxjs": "7.8.1", - "sass": "1.69.5", - "sass-loader": "13.3.2", + "sass": "1.69.7", + "sass-loader": "13.3.3", "semver": "7.5.4", - "source-map-loader": "4.0.1", + "source-map-loader": "5.0.0", "source-map-support": "0.5.21", - "terser": "5.24.0", + "terser": "5.26.0", "text-table": "0.2.0", "tree-kill": "1.2.2", "tslib": "2.6.2", - "undici": "5.27.2", - "vite": "4.5.1", + "undici": "6.2.1", + "vite": "5.0.12", + "watchpack": "2.4.0", "webpack": "5.89.0", "webpack-dev-middleware": "6.1.1", "webpack-dev-server": "4.15.1", @@ -228,20 +227,22 @@ "yarn": ">= 1.13.0" }, "optionalDependencies": { - "esbuild": "0.19.5" + "esbuild": "0.19.11" }, "peerDependencies": { "@angular/compiler-cli": "^17.0.0", "@angular/localize": "^17.0.0", "@angular/platform-server": "^17.0.0", "@angular/service-worker": "^17.0.0", + "@web/test-runner": "^0.18.0", + "browser-sync": "^3.0.2", "jest": "^29.5.0", "jest-environment-jsdom": "^29.5.0", "karma": "^6.3.0", "ng-packagr": "^17.0.0", "protractor": "^7.0.0", "tailwindcss": "^2.0.0 || ^3.0.0", - "typescript": ">=5.2 <5.3" + "typescript": ">=5.2 <5.4" }, "peerDependenciesMeta": { "@angular/localize": { @@ -253,6 +254,12 @@ "@angular/service-worker": { "optional": true }, + "@web/test-runner": { + "optional": true + }, + "browser-sync": { + "optional": true + }, "jest": { "optional": true }, @@ -273,13 +280,74 @@ } } }, - "node_modules/@angular-devkit/build-webpack": { - "version": "0.1700.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1700.8.tgz", - "integrity": "sha512-GA7QlCAlYB3uBkRaUYgIC/Vfajb9jMmouwYiAAEm34ZyP3ThFjdqsYd/A/exnuESt5o6Bh++C/PI34sV3lawRA==", + "node_modules/@angular-devkit/build-angular/node_modules/@babel/core": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz", + "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1700.8", + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.7", + "@babel/parser": "^7.23.6", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@angular-devkit/build-webpack": { + "version": "0.1701.2", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1701.2.tgz", + "integrity": "sha512-LqfSO5iTbiYByDadUET/8uIun8vSHMEdtoxiil/kdZ5T0NG0p7K8QqUMnWgg6suwO6kFfYJkMiS8Dq3Y/ONUNQ==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": "0.1701.2", "rxjs": "7.8.1" }, "engines": { @@ -293,9 +361,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.0.8.tgz", - "integrity": "sha512-gI8+SOwGUwr0WOlFrhLjohLolMzcguuoR0LTZEcGjdXvQyPgH4NDSRIIrfWCdu+ZVhfy76o3zQYdYc9QN8NrjQ==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.1.2.tgz", + "integrity": "sha512-ku+/W/HMCBacSWFppenr9y6Lx8mDuTuQvn1IkTyBLiJOpWnzgVbx9kHDeaDchGa1PwLlJUBBrv27t3qgJOIDPw==", "dev": true, "dependencies": { "ajv": "8.12.0", @@ -320,12 +388,12 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.0.8.tgz", - "integrity": "sha512-syo814SVWfJvne448IijjZvpWbuqJsEutdNqHWLTewTfX2U3KrIAr/XRVcXQMuyMvLCDiuxjMgEJxOIP7mcIPw==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.1.2.tgz", + "integrity": "sha512-8S9RuM8olFN/gwN+mjbuF1CwHX61f0i59EGXz9tXLnKRUTjsRR+8vVMTAmX0dvVAT5fJTG/T69X+HX7FeumdqA==", "dev": true, "dependencies": { - "@angular-devkit/core": "17.0.8", + "@angular-devkit/core": "17.1.2", "jsonc-parser": "3.2.0", "magic-string": "0.30.5", "ora": "5.4.1", @@ -338,13 +406,13 @@ } }, "node_modules/@angular-eslint/builder": { - "version": "17.1.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-17.1.1.tgz", - "integrity": "sha512-QGnIaypNP1osDObTIRJ5JF1KdMBn2oghZXMZAFN+qc+4+EX0SLfrSVw0YTZRH1Sg8ns3/Q+E6jYrswrhV1JmKQ==", + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-17.2.1.tgz", + "integrity": "sha512-O30eaR0wCPiP+zKWvXj2JM8hVq30Wok2rp7zJMFm3PurjF9nWIIyexXkE5fa538DYZYxu8N3gQRqhpv5jvTXCg==", "dev": true, "dependencies": { - "@nx/devkit": "17.1.3", - "nx": "17.1.3" + "@nx/devkit": "17.2.8", + "nx": "17.2.8" }, "peerDependencies": { "eslint": "^7.20.0 || ^8.0.0", @@ -352,19 +420,19 @@ } }, "node_modules/@angular-eslint/bundled-angular-compiler": { - "version": "17.1.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-17.1.1.tgz", - "integrity": "sha512-xRlSh9qjdUdUKAy/0UQsxX7wf1tHApAsHsfismebPriqfmVAPyEg4HBrM8ImWaZxiqaTGC1AyHsUBQD5FK8o6w==", + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-17.2.1.tgz", + "integrity": "sha512-puC0itsZv2QlrDOCcWtq1KZH+DvfrpV+mV78HHhi6+h25R5iIhr8ARKcl3EQxFjvrFq34jhG8pSupxKvFbHVfA==", "dev": true }, "node_modules/@angular-eslint/eslint-plugin": { - "version": "17.1.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-17.1.1.tgz", - "integrity": "sha512-fFOBlCOVObVu3gjLj+0BypqO1ZR/0bfJnDElqMdYwJG7zRaFT8NNQbrOo/q/GQoqOFoNna6mw3teTGsd5JnL2A==", + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-17.2.1.tgz", + "integrity": "sha512-9yA81BHpsaCUKRBtHGN3ieAy8HpIoffzPQMu34lYqZFT4yGHGhYmhQjNSQGBRbV2LD9dVv2U35rMHNmUcozXpw==", "dev": true, "dependencies": { - "@angular-eslint/utils": "17.1.1", - "@typescript-eslint/utils": "6.13.1" + "@angular-eslint/utils": "17.2.1", + "@typescript-eslint/utils": "6.19.0" }, "peerDependencies": { "eslint": "^7.20.0 || ^8.0.0", @@ -372,15 +440,15 @@ } }, "node_modules/@angular-eslint/eslint-plugin-template": { - "version": "17.1.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-17.1.1.tgz", - "integrity": "sha512-unZ6QNwtxuB8Eni7UPdw7uK6iZipZUXIsH+ZuLMOxwFgGMqeRnpv8SW0212rto3d/Ec0jESzVHKcwZ9pT+jxgw==", + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-17.2.1.tgz", + "integrity": "sha512-hl1hcHtcm90wyVL1OQGTz16oA0KHon+FFb3Qg0fLXObaXxA495Ecefd9ub5Xxg4JEOPRDi29bF1Y3YKpwflgeg==", "dev": true, "dependencies": { - "@angular-eslint/bundled-angular-compiler": "17.1.1", - "@angular-eslint/utils": "17.1.1", - "@typescript-eslint/type-utils": "6.13.1", - "@typescript-eslint/utils": "6.13.1", + "@angular-eslint/bundled-angular-compiler": "17.2.1", + "@angular-eslint/utils": "17.2.1", + "@typescript-eslint/type-utils": "6.19.0", + "@typescript-eslint/utils": "6.19.0", "aria-query": "5.3.0", "axobject-query": "4.0.0" }, @@ -389,17 +457,293 @@ "typescript": "*" } }, - "node_modules/@angular-eslint/schematics": { - "version": "17.1.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-17.1.1.tgz", - "integrity": "sha512-Bkt8iOXWRQGSrcLRGzdyJLvSPcIChW5+dh5lXa5GhdLmVAF7jpjxqGwW0rNb5JhLa/phyH0XQIpLBaOPtacSMA==", + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/@angular-eslint/utils": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-17.2.1.tgz", + "integrity": "sha512-qQYTBXy90dWM7fhhpa5i9lTtqqhJisvRa+naCrQx9kBgR458JScLdkVIdcZ9D/rPiDCmKiVUfgcDISnjUeqTqg==", "dev": true, "dependencies": { - "@angular-eslint/eslint-plugin": "17.1.1", - "@angular-eslint/eslint-plugin-template": "17.1.1", - "@nx/devkit": "17.1.3", + "@angular-eslint/bundled-angular-compiler": "17.2.1", + "@typescript-eslint/utils": "6.19.0" + }, + "peerDependencies": { + "eslint": "^7.20.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/@typescript-eslint/scope-manager": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.0.tgz", + "integrity": "sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/@typescript-eslint/types": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.0.tgz", + "integrity": "sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.0.tgz", + "integrity": "sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/@typescript-eslint/utils": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.19.0.tgz", + "integrity": "sha512-QR41YXySiuN++/dC9UArYOg4X86OAYP83OWTewpVx5ct1IZhjjgTLocj7QNxGhWoTqknsgpl7L+hGygCO+sdYw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/typescript-estree": "6.19.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz", + "integrity": "sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.19.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@angular-eslint/eslint-plugin/node_modules/@angular-eslint/utils": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-17.2.1.tgz", + "integrity": "sha512-qQYTBXy90dWM7fhhpa5i9lTtqqhJisvRa+naCrQx9kBgR458JScLdkVIdcZ9D/rPiDCmKiVUfgcDISnjUeqTqg==", + "dev": true, + "dependencies": { + "@angular-eslint/bundled-angular-compiler": "17.2.1", + "@typescript-eslint/utils": "6.19.0" + }, + "peerDependencies": { + "eslint": "^7.20.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.0.tgz", + "integrity": "sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@angular-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.0.tgz", + "integrity": "sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@angular-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.0.tgz", + "integrity": "sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@angular-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.19.0.tgz", + "integrity": "sha512-QR41YXySiuN++/dC9UArYOg4X86OAYP83OWTewpVx5ct1IZhjjgTLocj7QNxGhWoTqknsgpl7L+hGygCO+sdYw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/typescript-estree": "6.19.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@angular-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz", + "integrity": "sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.19.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@angular-eslint/eslint-plugin/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@angular-eslint/eslint-plugin/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@angular-eslint/schematics": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-17.2.1.tgz", + "integrity": "sha512-7ldtIePI4ZTp/TBpeOZkzfv30HSAn//4TgtFuqvojudI8n8batV5FqQ0VNm1e0zitl75t8Zwtr0KYT4I6vh59g==", + "dev": true, + "dependencies": { + "@angular-eslint/eslint-plugin": "17.2.1", + "@angular-eslint/eslint-plugin-template": "17.2.1", + "@nx/devkit": "17.2.8", "ignore": "5.3.0", - "nx": "17.1.3", + "nx": "17.2.8", "strip-json-comments": "3.1.1", "tmp": "0.2.1" }, @@ -408,37 +752,39 @@ } }, "node_modules/@angular-eslint/template-parser": { - "version": "17.1.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-17.1.1.tgz", - "integrity": "sha512-ofL46rNhRVeSxrSQF0vwhKMco+vJuo+ZGjSOzFmT9N3KAMB0j+WXTbpyGGMy0gQSBc4W6p+j+zxGa2CR2xb6wA==", + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-17.2.1.tgz", + "integrity": "sha512-WPQYFvRju0tCDXQ/pwrzC911pE07JvpeDgcN2elhzV6lxDHJEZpA5O9pnW9qgNA6J6XM9Q7dBkJ22ztAzC4WFw==", "dev": true, "dependencies": { - "@angular-eslint/bundled-angular-compiler": "17.1.1", - "eslint-scope": "^7.0.0" + "@angular-eslint/bundled-angular-compiler": "17.2.1", + "eslint-scope": "^8.0.0" }, "peerDependencies": { "eslint": "^7.20.0 || ^8.0.0", "typescript": "*" } }, - "node_modules/@angular-eslint/utils": { - "version": "17.1.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-17.1.1.tgz", - "integrity": "sha512-CTNPOb05S/DII/Fm8JYUvKo+B4u/ctHjGJ0X1YXUR0q31oaGqTE3KePGq76+Y6swRDf9NjUIcfcnZp3u3j4CBQ==", + "node_modules/@angular-eslint/template-parser/node_modules/eslint-scope": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.0.tgz", + "integrity": "sha512-zj3Byw6jX4TcFCJmxOzLt6iol5FAr9xQyZZSQjEzW2UiCJXLwXdRIKCYVFftnpZckaC9Ps9xlC7jB8tSeWWOaw==", "dev": true, "dependencies": { - "@angular-eslint/bundled-angular-compiler": "17.1.1", - "@typescript-eslint/utils": "6.13.1" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, - "peerDependencies": { - "eslint": "^7.20.0 || ^8.0.0", - "typescript": "*" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/@angular/cdk": { - "version": "17.0.4", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-17.0.4.tgz", - "integrity": "sha512-mh/EuIR0NPfpNqAXBSZWuJeBMXUvUDYdKhiFWZet5NLO1bDgFe1MGLBjtW4us95k4BZsMLbCKNxJgc+4JqwUvg==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-17.1.2.tgz", + "integrity": "sha512-eu9D60RQv213qi7oh6ae9Z+d6+AG/aqi0y70Ag9BjwqTiatDiYvSySxswxYYKdzPp0hx0ZUTGi16LqtT6pyj6Q==", "dependencies": { "tslib": "^2.3.0" }, @@ -452,25 +798,25 @@ } }, "node_modules/@angular/cli": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-17.0.8.tgz", - "integrity": "sha512-yZXYNLAFv9u2qypsVqtS+rRCsnjsIPYXr6TcI/r5buzOtC7UQ2lleYsWJqX47SsyGMk/o3gaYg5Bj2I5mmRDLA==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-17.1.2.tgz", + "integrity": "sha512-U1W6XZNrfeRkXW2fO3AU25rRttqZahVkhzcK3lAtJ8+lSrStCOF7x1gz6tmFZFte1fNHQrXqD0yIDkd8H2/cvw==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1700.8", - "@angular-devkit/core": "17.0.8", - "@angular-devkit/schematics": "17.0.8", - "@schematics/angular": "17.0.8", + "@angular-devkit/architect": "0.1701.2", + "@angular-devkit/core": "17.1.2", + "@angular-devkit/schematics": "17.1.2", + "@schematics/angular": "17.1.2", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.3", "ini": "4.1.1", - "inquirer": "9.2.11", + "inquirer": "9.2.12", "jsonc-parser": "3.2.0", "npm-package-arg": "11.0.1", "npm-pick-manifest": "9.0.0", "open": "8.4.2", "ora": "5.4.1", - "pacote": "17.0.4", + "pacote": "17.0.5", "resolve": "1.22.8", "semver": "7.5.4", "symbol-observable": "4.0.0", @@ -486,9 +832,9 @@ } }, "node_modules/@angular/common": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-17.0.8.tgz", - "integrity": "sha512-fFfwtdg7H+OkqnvV/ENu8F8KGfgIiH16DDbQqYY5KQyyQB+SMsoVW29F1fGx6Y30s7ZlsLOy6cHhgrw74itkSw==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-17.1.2.tgz", + "integrity": "sha512-y/wD+zuPaPgK3dB80Q63qBtuu5TuryKuUgjWrOmrguBWV9oiJRhKQrcp1gVw9vVrowmbDBKGtPMS622Q4oxOWQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -496,14 +842,14 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/core": "17.0.8", + "@angular/core": "17.1.2", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-17.0.8.tgz", - "integrity": "sha512-48jWypuhBGTrUUbkz1vB9gjbKKZ3hpuJ2DUUncd331Yw4tqkqZQbBa/E3ei4IHiCxEvW2uX3lI4AwlhuozmUtA==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-17.1.2.tgz", + "integrity": "sha512-1vJuQRM5V01nC6qsLvBKrHVZXpzbK0YKubwVQUXCSfDNZBcDFak3SQcwU4C2t880rU3ZvFDB1UWfk7CKn5w9Kw==", "dependencies": { "tslib": "^2.3.0" }, @@ -511,7 +857,7 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/core": "17.0.8" + "@angular/core": "17.1.2" }, "peerDependenciesMeta": { "@angular/core": { @@ -520,9 +866,9 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-17.0.8.tgz", - "integrity": "sha512-ny2SMVgl+icjMuU5ZM57yFGUrhjR0hNxfCn0otAD3jUFliz/Onu9l6EPRKA5Cr8MZx3mg3rTLSBMD17YT8rsOg==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-17.1.2.tgz", + "integrity": "sha512-4P4ttCe4IF9yq7bxCDxbVW7purN7qV0nqofP5Tth1xCsgIJeGmOMMQJN5RJCZNrAPMkvMv39eV878sgcDjbpOA==", "dependencies": { "@babel/core": "7.23.2", "@jridgewell/sourcemap-codec": "^1.4.14", @@ -542,14 +888,14 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/compiler": "17.0.8", - "typescript": ">=5.2 <5.3" + "@angular/compiler": "17.1.2", + "typescript": ">=5.2 <5.4" } }, "node_modules/@angular/core": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-17.0.8.tgz", - "integrity": "sha512-tzYsK24LdkNuKNJK6efF4XOqspvF/qOe9j/n1Y61a6mNvFwsJFGbcmdZMby4hI/YRm6oIDoIIFjSep8ycp6Pbw==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-17.1.2.tgz", + "integrity": "sha512-0M787BZVgYSVogHCUzo/dFrT56TgfQoEsOQngHMpyERJZv6dycXZlRdHc6TzvHUa+Uu/MNjn/RclBR8063bdWA==", "dependencies": { "tslib": "^2.3.0" }, @@ -562,9 +908,9 @@ } }, "node_modules/@angular/forms": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-17.0.8.tgz", - "integrity": "sha512-WZBHbMQjaSovAzOMhKqZN+m7eUPGfOzh9rKFKvj6UQLIJ9qSpEpqlvL0omU1z/47s3XXeLiBzomMiRfQISJvvw==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-17.1.2.tgz", + "integrity": "sha512-n1WsZAL2IVOB6ocROKR6CFOR14PIC9RGAB41SwTfPhJeBM1kjW48bXY0sw97TasxM4mWJKGCmFXu0jQwkoeSpQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -572,18 +918,19 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/common": "17.0.8", - "@angular/core": "17.0.8", - "@angular/platform-browser": "17.0.8", + "@angular/common": "17.1.2", + "@angular/core": "17.1.2", + "@angular/platform-browser": "17.1.2", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/localize": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-17.0.8.tgz", - "integrity": "sha512-1zW8qWKNMH3r/x4KpwzzUmVY+iN76vYdhjA6gzZDnpJxpon9eyljNEildj9+zSWeNUr2LgJ6HnkIX9q1f3mXfA==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-17.1.2.tgz", + "integrity": "sha512-9HE1vrgcz90EgU/eZKtUZJlec5kePvTj3pNim1/FLsEjDpN8sBn1QYrPTIWDCySSXPAkMfAcZBODYdPBXWVi8A==", "dependencies": { "@babel/core": "7.23.2", + "@types/babel__core": "7.20.2", "fast-glob": "3.3.1", "yargs": "^17.2.1" }, @@ -596,14 +943,26 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/compiler": "17.0.8", - "@angular/compiler-cli": "17.0.8" + "@angular/compiler": "17.1.2", + "@angular/compiler-cli": "17.1.2" + } + }, + "node_modules/@angular/localize/node_modules/@types/babel__core": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", + "integrity": "sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" } }, "node_modules/@angular/platform-browser": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.0.8.tgz", - "integrity": "sha512-XaI+p2AxQaIHzR761lhPUf4OcOp46WDW0IfbvOzaezHE+8r81joZyVSDQPgXSa/aRfI58YhcfUavuGqyU3PphA==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.1.2.tgz", + "integrity": "sha512-unfpA5OLnqDmDb/oAQR2t2iROpOg02qwZayxyFg4MUZdDdnghPCfX77L2sr6oVVa7OJfKYFlmwmBXX1H3zjcXA==", "dependencies": { "tslib": "^2.3.0" }, @@ -611,9 +970,9 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/animations": "17.0.8", - "@angular/common": "17.0.8", - "@angular/core": "17.0.8" + "@angular/animations": "17.1.2", + "@angular/common": "17.1.2", + "@angular/core": "17.1.2" }, "peerDependenciesMeta": { "@angular/animations": { @@ -622,9 +981,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-17.0.8.tgz", - "integrity": "sha512-BIXNKnfBZb8sdluQ7WIhIXFuVnsJJ0SV+aiMKzQ7B6XhWoAXZQnlvON2thydjIIVuCvaF3YmWTbILI2K8YZ2jQ==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-17.1.2.tgz", + "integrity": "sha512-xiWVDHbA+owDhKo5SAnzZtawA1ktGthlCl3YTI+vmkJpF6axkYOqR7YL+aEQX/y/5GSK+oR+03SgAnYcpOwKlQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -632,16 +991,16 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/common": "17.0.8", - "@angular/compiler": "17.0.8", - "@angular/core": "17.0.8", - "@angular/platform-browser": "17.0.8" + "@angular/common": "17.1.2", + "@angular/compiler": "17.1.2", + "@angular/core": "17.1.2", + "@angular/platform-browser": "17.1.2" } }, "node_modules/@angular/router": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-17.0.8.tgz", - "integrity": "sha512-ptphcRe1RG/mIS60R7ZPilkkrxautqB0sOhds3h5VP3g628G1a2HWzvnmvjEfpJWDMFivV32VJMMBtTLqGr+0Q==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-17.1.2.tgz", + "integrity": "sha512-8OexxiiscRdfEiB6jOKlZFyAKZtvIQvh0ugW6U7nAXPV5XsA2UL80sXkc829eH0DnJn2Wj/HS6ZNGgG81PWDHg==", "dependencies": { "tslib": "^2.3.0" }, @@ -649,9 +1008,9 @@ "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/common": "17.0.8", - "@angular/core": "17.0.8", - "@angular/platform-browser": "17.0.8", + "@angular/common": "17.1.2", + "@angular/core": "17.1.2", + "@angular/platform-browser": "17.1.2", "rxjs": "^6.5.3 || ^7.4.0" } }, @@ -724,11 +1083,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dependencies": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -785,9 +1144,9 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.6.tgz", - "integrity": "sha512-cBXU1vZni/CpGF29iTu4YRbOZt3Wat6zCoMDxRF1MayiEc4URxOj31tT65HUM0CRpMowA3HCJaAOVOUnMf96cw==", + "version": "7.23.10", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.10.tgz", + "integrity": "sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", @@ -843,9 +1202,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.4.tgz", - "integrity": "sha512-QcJMILQCu2jm5TFPGA3lCpJJTeEP+mqeXooG/NZbg/h5FTFi6V0+99ahlRsW8/kRLyb24LZVCCiclDedhLKcBA==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", + "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", @@ -1058,13 +1417,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.6.tgz", - "integrity": "sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.9.tgz", + "integrity": "sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==", "dependencies": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.6", - "@babel/types": "^7.23.6" + "@babel/template": "^7.23.9", + "@babel/traverse": "^7.23.9", + "@babel/types": "^7.23.9" }, "engines": { "node": ">=6.9.0" @@ -1084,9 +1443,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", - "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", + "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", "bin": { "parser": "bin/babel-parser.js" }, @@ -1126,6 +1485,22 @@ "@babel/core": "^7.13.0" } }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz", + "integrity": "sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", @@ -1431,9 +1806,9 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.2.tgz", - "integrity": "sha512-BBYVGxbDVHfoeXbOwcagAkOQAm9NxoTdMGfTqghu1GrvadSaw6iW3Je6IcL5PNOw8VwjxqBECXy50/iCQSY/lQ==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.7.tgz", + "integrity": "sha512-PdxEpL71bJp1byMG0va5gwQcXHxuEYC/BgI/e88mGTtohbZN28O5Yit0Plkkm/dBzCF/BxmbNcses1RH1T+urA==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", @@ -1449,14 +1824,14 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", - "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", + "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.5" + "@babel/helper-remap-async-to-generator": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -1529,16 +1904,15 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.5.tgz", - "integrity": "sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==", + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz", + "integrity": "sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-split-export-declaration": "^7.22.6", @@ -1790,9 +2164,9 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz", - "integrity": "sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.9.tgz", + "integrity": "sha512-KDlPRM6sLo4o1FkiSlXoAa8edLXFsKKIda779fbLrvmeuc3itnjCtaO6RrtoaANsIJANj+Vk1zqbZIMhkCAHVw==", "dev": true, "dependencies": { "@babel/helper-hoist-variables": "^7.22.5", @@ -2050,16 +2424,16 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.2.tgz", - "integrity": "sha512-XOntj6icgzMS58jPVtQpiuF6ZFWxQiJavISGx5KGjRj+3gqZr8+N6Kx+N9BApWzgS+DOjIZfXXj0ZesenOWDyA==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.7.tgz", + "integrity": "sha512-fa0hnfmiXc9fq/weK34MUV0drz2pOL/vfKWvN7Qw127hiUPabFCUMgAbYWcchRzMJit4o5ARsK/s+5h0249pLw==", "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.6", - "babel-plugin-polyfill-corejs3": "^0.8.5", - "babel-plugin-polyfill-regenerator": "^0.5.3", + "babel-plugin-polyfill-corejs2": "^0.4.7", + "babel-plugin-polyfill-corejs3": "^0.8.7", + "babel-plugin-polyfill-regenerator": "^0.5.4", "semver": "^6.3.1" }, "engines": { @@ -2218,25 +2592,26 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.2.tgz", - "integrity": "sha512-BW3gsuDD+rvHL2VO2SjAUNTBe5YrjsTiDyqamPDWY723na3/yPQ65X5oQkFVJZ0o50/2d+svm1rkPoJeR1KxVQ==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.7.tgz", + "integrity": "sha512-SY27X/GtTz/L4UryMNJ6p4fH4nsgWbz84y9FE0bQeWJP6O5BhgVCt53CotQKHCOeXJel8VyhlhujhlltKms/CA==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.23.2", - "@babel/helper-compilation-targets": "^7.22.15", + "@babel/compat-data": "^7.23.5", + "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.15", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.15", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.7", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.22.5", - "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-assertions": "^7.23.3", + "@babel/plugin-syntax-import-attributes": "^7.23.3", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", @@ -2248,59 +2623,58 @@ "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.22.5", - "@babel/plugin-transform-async-generator-functions": "^7.23.2", - "@babel/plugin-transform-async-to-generator": "^7.22.5", - "@babel/plugin-transform-block-scoped-functions": "^7.22.5", - "@babel/plugin-transform-block-scoping": "^7.23.0", - "@babel/plugin-transform-class-properties": "^7.22.5", - "@babel/plugin-transform-class-static-block": "^7.22.11", - "@babel/plugin-transform-classes": "^7.22.15", - "@babel/plugin-transform-computed-properties": "^7.22.5", - "@babel/plugin-transform-destructuring": "^7.23.0", - "@babel/plugin-transform-dotall-regex": "^7.22.5", - "@babel/plugin-transform-duplicate-keys": "^7.22.5", - "@babel/plugin-transform-dynamic-import": "^7.22.11", - "@babel/plugin-transform-exponentiation-operator": "^7.22.5", - "@babel/plugin-transform-export-namespace-from": "^7.22.11", - "@babel/plugin-transform-for-of": "^7.22.15", - "@babel/plugin-transform-function-name": "^7.22.5", - "@babel/plugin-transform-json-strings": "^7.22.11", - "@babel/plugin-transform-literals": "^7.22.5", - "@babel/plugin-transform-logical-assignment-operators": "^7.22.11", - "@babel/plugin-transform-member-expression-literals": "^7.22.5", - "@babel/plugin-transform-modules-amd": "^7.23.0", - "@babel/plugin-transform-modules-commonjs": "^7.23.0", - "@babel/plugin-transform-modules-systemjs": "^7.23.0", - "@babel/plugin-transform-modules-umd": "^7.22.5", + "@babel/plugin-transform-arrow-functions": "^7.23.3", + "@babel/plugin-transform-async-generator-functions": "^7.23.7", + "@babel/plugin-transform-async-to-generator": "^7.23.3", + "@babel/plugin-transform-block-scoped-functions": "^7.23.3", + "@babel/plugin-transform-block-scoping": "^7.23.4", + "@babel/plugin-transform-class-properties": "^7.23.3", + "@babel/plugin-transform-class-static-block": "^7.23.4", + "@babel/plugin-transform-classes": "^7.23.5", + "@babel/plugin-transform-computed-properties": "^7.23.3", + "@babel/plugin-transform-destructuring": "^7.23.3", + "@babel/plugin-transform-dotall-regex": "^7.23.3", + "@babel/plugin-transform-duplicate-keys": "^7.23.3", + "@babel/plugin-transform-dynamic-import": "^7.23.4", + "@babel/plugin-transform-exponentiation-operator": "^7.23.3", + "@babel/plugin-transform-export-namespace-from": "^7.23.4", + "@babel/plugin-transform-for-of": "^7.23.6", + "@babel/plugin-transform-function-name": "^7.23.3", + "@babel/plugin-transform-json-strings": "^7.23.4", + "@babel/plugin-transform-literals": "^7.23.3", + "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", + "@babel/plugin-transform-member-expression-literals": "^7.23.3", + "@babel/plugin-transform-modules-amd": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-modules-systemjs": "^7.23.3", + "@babel/plugin-transform-modules-umd": "^7.23.3", "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.22.5", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.11", - "@babel/plugin-transform-numeric-separator": "^7.22.11", - "@babel/plugin-transform-object-rest-spread": "^7.22.15", - "@babel/plugin-transform-object-super": "^7.22.5", - "@babel/plugin-transform-optional-catch-binding": "^7.22.11", - "@babel/plugin-transform-optional-chaining": "^7.23.0", - "@babel/plugin-transform-parameters": "^7.22.15", - "@babel/plugin-transform-private-methods": "^7.22.5", - "@babel/plugin-transform-private-property-in-object": "^7.22.11", - "@babel/plugin-transform-property-literals": "^7.22.5", - "@babel/plugin-transform-regenerator": "^7.22.10", - "@babel/plugin-transform-reserved-words": "^7.22.5", - "@babel/plugin-transform-shorthand-properties": "^7.22.5", - "@babel/plugin-transform-spread": "^7.22.5", - "@babel/plugin-transform-sticky-regex": "^7.22.5", - "@babel/plugin-transform-template-literals": "^7.22.5", - "@babel/plugin-transform-typeof-symbol": "^7.22.5", - "@babel/plugin-transform-unicode-escapes": "^7.22.10", - "@babel/plugin-transform-unicode-property-regex": "^7.22.5", - "@babel/plugin-transform-unicode-regex": "^7.22.5", - "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.23.3", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", + "@babel/plugin-transform-numeric-separator": "^7.23.4", + "@babel/plugin-transform-object-rest-spread": "^7.23.4", + "@babel/plugin-transform-object-super": "^7.23.3", + "@babel/plugin-transform-optional-catch-binding": "^7.23.4", + "@babel/plugin-transform-optional-chaining": "^7.23.4", + "@babel/plugin-transform-parameters": "^7.23.3", + "@babel/plugin-transform-private-methods": "^7.23.3", + "@babel/plugin-transform-private-property-in-object": "^7.23.4", + "@babel/plugin-transform-property-literals": "^7.23.3", + "@babel/plugin-transform-regenerator": "^7.23.3", + "@babel/plugin-transform-reserved-words": "^7.23.3", + "@babel/plugin-transform-shorthand-properties": "^7.23.3", + "@babel/plugin-transform-spread": "^7.23.3", + "@babel/plugin-transform-sticky-regex": "^7.23.3", + "@babel/plugin-transform-template-literals": "^7.23.3", + "@babel/plugin-transform-typeof-symbol": "^7.23.3", + "@babel/plugin-transform-unicode-escapes": "^7.23.3", + "@babel/plugin-transform-unicode-property-regex": "^7.23.3", + "@babel/plugin-transform-unicode-regex": "^7.23.3", + "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", "@babel/preset-modules": "0.1.6-no-external-plugins", - "@babel/types": "^7.23.0", - "babel-plugin-polyfill-corejs2": "^0.4.6", - "babel-plugin-polyfill-corejs3": "^0.8.5", - "babel-plugin-polyfill-regenerator": "^0.5.3", + "babel-plugin-polyfill-corejs2": "^0.4.7", + "babel-plugin-polyfill-corejs3": "^0.8.7", + "babel-plugin-polyfill-regenerator": "^0.5.4", "core-js-compat": "^3.31.0", "semver": "^6.3.1" }, @@ -2341,9 +2715,9 @@ "dev": true }, "node_modules/@babel/runtime": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", - "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.7.tgz", + "integrity": "sha512-w06OXVOFso7LcbzMiDGt+3X7Rh7Ho8MmgPoWU3rarH+8upf+wSU/grlGbWzQyr3DkdN6ZeuMFjpdwW0Q+HxobA==", "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" @@ -2353,22 +2727,22 @@ } }, "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz", + "integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.23.9", + "@babel/types": "^7.23.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.6.tgz", - "integrity": "sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.9.tgz", + "integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==", "dependencies": { "@babel/code-frame": "^7.23.5", "@babel/generator": "^7.23.6", @@ -2376,8 +2750,8 @@ "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.6", - "@babel/types": "^7.23.6", + "@babel/parser": "^7.23.9", + "@babel/types": "^7.23.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2385,24 +2759,10 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", - "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", - "dependencies": { - "@babel/types": "^7.23.6", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/types": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", - "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", + "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", "dependencies": { "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", @@ -2457,10 +2817,26 @@ "node": ">=10.0.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz", + "integrity": "sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@esbuild/android-arm": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.5.tgz", - "integrity": "sha512-bhvbzWFF3CwMs5tbjf3ObfGqbl/17ict2/uwOSfr3wmxDE6VdS2GqY/FuzIPe0q0bdhj65zQsvqfArI9MY6+AA==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.11.tgz", + "integrity": "sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw==", "cpu": [ "arm" ], @@ -2474,9 +2850,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.5.tgz", - "integrity": "sha512-5d1OkoJxnYQfmC+Zd8NBFjkhyCNYwM4n9ODrycTFY6Jk1IGiZ+tjVJDDSwDt77nK+tfpGP4T50iMtVi4dEGzhQ==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.11.tgz", + "integrity": "sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q==", "cpu": [ "arm64" ], @@ -2490,9 +2866,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.5.tgz", - "integrity": "sha512-9t+28jHGL7uBdkBjL90QFxe7DVA+KGqWlHCF8ChTKyaKO//VLuoBricQCgwhOjA1/qOczsw843Fy4cbs4H3DVA==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.11.tgz", + "integrity": "sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg==", "cpu": [ "x64" ], @@ -2506,9 +2882,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.5.tgz", - "integrity": "sha512-mvXGcKqqIqyKoxq26qEDPHJuBYUA5KizJncKOAf9eJQez+L9O+KfvNFu6nl7SCZ/gFb2QPaRqqmG0doSWlgkqw==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.11.tgz", + "integrity": "sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==", "cpu": [ "arm64" ], @@ -2522,9 +2898,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.5.tgz", - "integrity": "sha512-Ly8cn6fGLNet19s0X4unjcniX24I0RqjPv+kurpXabZYSXGM4Pwpmf85WHJN3lAgB8GSth7s5A0r856S+4DyiA==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.11.tgz", + "integrity": "sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g==", "cpu": [ "x64" ], @@ -2538,9 +2914,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.5.tgz", - "integrity": "sha512-GGDNnPWTmWE+DMchq1W8Sd0mUkL+APvJg3b11klSGUDvRXh70JqLAO56tubmq1s2cgpVCSKYywEiKBfju8JztQ==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.11.tgz", + "integrity": "sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA==", "cpu": [ "arm64" ], @@ -2554,9 +2930,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.5.tgz", - "integrity": "sha512-1CCwDHnSSoA0HNwdfoNY0jLfJpd7ygaLAp5EHFos3VWJCRX9DMwWODf96s9TSse39Br7oOTLryRVmBoFwXbuuQ==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.11.tgz", + "integrity": "sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw==", "cpu": [ "x64" ], @@ -2570,9 +2946,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.5.tgz", - "integrity": "sha512-lrWXLY/vJBzCPC51QN0HM71uWgIEpGSjSZZADQhq7DKhPcI6NH1IdzjfHkDQws2oNpJKpR13kv7/pFHBbDQDwQ==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.11.tgz", + "integrity": "sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q==", "cpu": [ "arm" ], @@ -2586,9 +2962,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.5.tgz", - "integrity": "sha512-o3vYippBmSrjjQUCEEiTZ2l+4yC0pVJD/Dl57WfPwwlvFkrxoSO7rmBZFii6kQB3Wrn/6GwJUPLU5t52eq2meA==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.11.tgz", + "integrity": "sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg==", "cpu": [ "arm64" ], @@ -2602,9 +2978,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.5.tgz", - "integrity": "sha512-MkjHXS03AXAkNp1KKkhSKPOCYztRtK+KXDNkBa6P78F8Bw0ynknCSClO/ztGszILZtyO/lVKpa7MolbBZ6oJtQ==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.11.tgz", + "integrity": "sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA==", "cpu": [ "ia32" ], @@ -2618,9 +2994,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.5.tgz", - "integrity": "sha512-42GwZMm5oYOD/JHqHska3Jg0r+XFb/fdZRX+WjADm3nLWLcIsN27YKtqxzQmGNJgu0AyXg4HtcSK9HuOk3v1Dw==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.11.tgz", + "integrity": "sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg==", "cpu": [ "loong64" ], @@ -2634,9 +3010,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.5.tgz", - "integrity": "sha512-kcjndCSMitUuPJobWCnwQ9lLjiLZUR3QLQmlgaBfMX23UEa7ZOrtufnRds+6WZtIS9HdTXqND4yH8NLoVVIkcg==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.11.tgz", + "integrity": "sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg==", "cpu": [ "mips64el" ], @@ -2650,9 +3026,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.5.tgz", - "integrity": "sha512-yJAxJfHVm0ZbsiljbtFFP1BQKLc8kUF6+17tjQ78QjqjAQDnhULWiTA6u0FCDmYT1oOKS9PzZ2z0aBI+Mcyj7Q==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.11.tgz", + "integrity": "sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA==", "cpu": [ "ppc64" ], @@ -2666,9 +3042,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.5.tgz", - "integrity": "sha512-5u8cIR/t3gaD6ad3wNt1MNRstAZO+aNyBxu2We8X31bA8XUNyamTVQwLDA1SLoPCUehNCymhBhK3Qim1433Zag==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.11.tgz", + "integrity": "sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ==", "cpu": [ "riscv64" ], @@ -2682,9 +3058,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.5.tgz", - "integrity": "sha512-Z6JrMyEw/EmZBD/OFEFpb+gao9xJ59ATsoTNlj39jVBbXqoZm4Xntu6wVmGPB/OATi1uk/DB+yeDPv2E8PqZGw==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.11.tgz", + "integrity": "sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q==", "cpu": [ "s390x" ], @@ -2698,9 +3074,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.5.tgz", - "integrity": "sha512-psagl+2RlK1z8zWZOmVdImisMtrUxvwereIdyJTmtmHahJTKb64pAcqoPlx6CewPdvGvUKe2Jw+0Z/0qhSbG1A==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.11.tgz", + "integrity": "sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA==", "cpu": [ "x64" ], @@ -2714,9 +3090,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.5.tgz", - "integrity": "sha512-kL2l+xScnAy/E/3119OggX8SrWyBEcqAh8aOY1gr4gPvw76la2GlD4Ymf832UCVbmuWeTf2adkZDK+h0Z/fB4g==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.11.tgz", + "integrity": "sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ==", "cpu": [ "x64" ], @@ -2730,9 +3106,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.5.tgz", - "integrity": "sha512-sPOfhtzFufQfTBgRnE1DIJjzsXukKSvZxloZbkJDG383q0awVAq600pc1nfqBcl0ice/WN9p4qLc39WhBShRTA==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.11.tgz", + "integrity": "sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw==", "cpu": [ "x64" ], @@ -2746,9 +3122,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.5.tgz", - "integrity": "sha512-dGZkBXaafuKLpDSjKcB0ax0FL36YXCvJNnztjKV+6CO82tTYVDSH2lifitJ29jxRMoUhgkg9a+VA/B03WK5lcg==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.11.tgz", + "integrity": "sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ==", "cpu": [ "x64" ], @@ -2762,9 +3138,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.5.tgz", - "integrity": "sha512-dWVjD9y03ilhdRQ6Xig1NWNgfLtf2o/STKTS+eZuF90fI2BhbwD6WlaiCGKptlqXlURVB5AUOxUj09LuwKGDTg==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.11.tgz", + "integrity": "sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ==", "cpu": [ "arm64" ], @@ -2778,9 +3154,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.5.tgz", - "integrity": "sha512-4liggWIA4oDgUxqpZwrDhmEfAH4d0iljanDOK7AnVU89T6CzHon/ony8C5LeOdfgx60x5cnQJFZwEydVlYx4iw==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.11.tgz", + "integrity": "sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg==", "cpu": [ "ia32" ], @@ -2794,9 +3170,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.5.tgz", - "integrity": "sha512-czTrygUsB/jlM8qEW5MD8bgYU2Xg14lo6kBDXW6HdxKjh8M5PzETGiSHaz9MtbXBYDloHNUAUW2tMiKW4KM9Mw==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.11.tgz", + "integrity": "sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==", "cpu": [ "x64" ], @@ -3938,9 +4314,9 @@ } }, "node_modules/@ng-select/ng-select": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/@ng-select/ng-select/-/ng-select-12.0.4.tgz", - "integrity": "sha512-bcvYLCdmKtJBZRLFLfnKauPqVlJJDecMzU4pZ360h1V8EsE4BCHoabNJQptqOESigcxBY1bpAe0i6aKu6JgL5Q==", + "version": "12.0.6", + "resolved": "https://registry.npmjs.org/@ng-select/ng-select/-/ng-select-12.0.6.tgz", + "integrity": "sha512-Xn3r10hQ60hXbS8tOiuT412OLYQrsS6QfGojuJBNy2Ns0rFrsZVV2T3TlBq75ZKXBTUgL9ZMQvCilx6z0SxkDw==", "dependencies": { "tslib": "^2.3.1" }, @@ -3970,9 +4346,9 @@ } }, "node_modules/@ngtools/webpack": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-17.0.8.tgz", - "integrity": "sha512-wx0XBMrbpDeailK2uIhp/ZVMC3GK3BWwJjUu5SbT4BFrcoi2Zd9/9m0RCBAY54UXLBCqKd+ih7pJ6JSvprZmWw==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-17.1.2.tgz", + "integrity": "sha512-MdNVSIp0x8AK26L+CxMTXH4weq2sNIp4C09RSdk7y6UkfBxMA3O0jTto9tW3ehkBaaGZ4dSiWkXA8L/ydMiQmA==", "dev": true, "engines": { "node": "^18.13.0 || >=20.9.0", @@ -3981,7 +4357,7 @@ }, "peerDependencies": { "@angular/compiler-cli": "^17.0.0", - "typescript": ">=5.2 <5.3", + "typescript": ">=5.2 <5.4", "webpack": "^5.54.0" } }, @@ -4047,9 +4423,9 @@ } }, "node_modules/@npmcli/agent/node_modules/lru-cache": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", - "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "dev": true, "engines": { "node": "14 || >=16.14" @@ -4068,9 +4444,9 @@ } }, "node_modules/@npmcli/git": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.3.tgz", - "integrity": "sha512-UZp9NwK+AynTrKvHn5k3KviW/hA5eENmFsu3iAPe7sWRt0lFUdsY/wXIYjpDFe7cdSNwOIzbObfwgt6eL5/2zw==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.4.tgz", + "integrity": "sha512-nr6/WezNzuYUppzXRaYu/W4aT5rLxdXqEFupbh6e/ovlYFQ8hpu1UUPV3Ir/YTl+74iXl2ZOMlGzudh9ZPUchQ==", "dev": true, "dependencies": { "@npmcli/promise-spawn": "^7.0.0", @@ -4096,9 +4472,9 @@ } }, "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", - "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "dev": true, "engines": { "node": "14 || >=16.14" @@ -4144,10 +4520,83 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/@npmcli/package-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.0.0.tgz", + "integrity": "sha512-OI2zdYBLhQ7kpNPaJxiflofYIpkNLi+lnGdzqUOfRmCF3r2l1nadcjtCYMJKv/Utm/ZtlffaUuTiAktPHbc17g==", + "dev": true, + "dependencies": { + "@npmcli/git": "^5.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^7.0.0", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/package-json/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@npmcli/package-json/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/package-json/node_modules/json-parse-even-better-errors": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", + "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/package-json/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@npmcli/promise-spawn": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.0.tgz", - "integrity": "sha512-wBqcGsMELZna0jDblGd7UXgOby45TQaMWmbFwWX+SEotk4HV6zG2t6rT9siyLhPk4P6YYqgfL1UO8nMWDBVJXQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.1.tgz", + "integrity": "sha512-P4KkF9jX3y+7yFUxgcUdDtLy+t4OlDGuEBLNs57AZsfSfg+uV6MLndqGpnl4831ggaEdXwR50XFoZP4VFtHolg==", "dev": true, "dependencies": { "which": "^4.0.0" @@ -4181,15 +4630,15 @@ } }, "node_modules/@npmcli/run-script": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-7.0.2.tgz", - "integrity": "sha512-Omu0rpA8WXvcGeY6DDzyRoY1i5DkCBkzyJ+m2u7PD6quzb0TvSqdIPOkTn8ZBOj7LbbcbMfZ3c5skwSu6m8y2w==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-7.0.4.tgz", + "integrity": "sha512-9ApYM/3+rBt9V80aYg6tZfzj3UWdiYyCt7gJUD1VJKvWF5nwKDSICXbYIQbspFTq6TOpbsEtIC0LArB8d9PFmg==", "dev": true, "dependencies": { "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.0.0", "@npmcli/promise-spawn": "^7.0.0", "node-gyp": "^10.0.0", - "read-package-json-fast": "^3.0.0", "which": "^4.0.0" }, "engines": { @@ -4221,21 +4670,21 @@ } }, "node_modules/@nrwl/devkit": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-17.1.3.tgz", - "integrity": "sha512-8HfIY7P3yIYfQ/XKuHoq0GGLA9GpwWtBlI9kPQ0ygjuJ9BkpiGMtQvO6003zs7c6vpc2vNeG+Jmi72+EKvoN5A==", + "version": "17.2.8", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-17.2.8.tgz", + "integrity": "sha512-l2dFy5LkWqSA45s6pee6CoqJeluH+sjRdVnAAQfjLHRNSx6mFAKblyzq5h1f4P0EUCVVVqLs+kVqmNx5zxYqvw==", "dev": true, "dependencies": { - "@nx/devkit": "17.1.3" + "@nx/devkit": "17.2.8" } }, "node_modules/@nrwl/tao": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-17.1.3.tgz", - "integrity": "sha512-9YpfEkUpVqOweqgQvMDcWApNx4jhCqBNH5IByZj302Enp3TLnQSvhuX5Dfr8hNQRQokIpEn6tW8SGTctTM5LXw==", + "version": "17.2.8", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-17.2.8.tgz", + "integrity": "sha512-Qpk5YKeJ+LppPL/wtoDyNGbJs2MsTi6qyX/RdRrEc8lc4bk6Cw3Oul1qTXCI6jT0KzTz+dZtd0zYD/G7okkzvg==", "dev": true, "dependencies": { - "nx": "17.1.3", + "nx": "17.2.8", "tslib": "^2.3.0" }, "bin": { @@ -4243,12 +4692,12 @@ } }, "node_modules/@nx/devkit": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-17.1.3.tgz", - "integrity": "sha512-1Is7ooovg3kdGJ5VdkePulRUDaMYLLULr+LwXgx7oHSW7AY2iCmhkoOE/vSR7DJ6rkey2gYx7eT1IoRoORiIaQ==", + "version": "17.2.8", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-17.2.8.tgz", + "integrity": "sha512-6LtiQihtZwqz4hSrtT5cCG5XMCWppG6/B8c1kNksg97JuomELlWyUyVF+sxmeERkcLYFaKPTZytP0L3dmCFXaw==", "dev": true, "dependencies": { - "@nrwl/devkit": "17.1.3", + "@nrwl/devkit": "17.2.8", "ejs": "^3.1.7", "enquirer": "~2.3.6", "ignore": "^5.0.4", @@ -4294,9 +4743,9 @@ "dev": true }, "node_modules/@nx/nx-darwin-arm64": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-17.1.3.tgz", - "integrity": "sha512-f4qLa0y3C4uuhYKgq+MU892WaQvtvmHqrEhHINUOxYXNiLy2sgyJPW0mOZvzXtC4dPaUmiVaFP5RMVzc8Lxhtg==", + "version": "17.2.8", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-17.2.8.tgz", + "integrity": "sha512-dMb0uxug4hM7tusISAU1TfkDK3ixYmzc1zhHSZwpR7yKJIyKLtUpBTbryt8nyso37AS1yH+dmfh2Fj2WxfBHTg==", "cpu": [ "arm64" ], @@ -4310,9 +4759,9 @@ } }, "node_modules/@nx/nx-darwin-x64": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-17.1.3.tgz", - "integrity": "sha512-kh76ZjqkLeQUIAfTa9G/DFFf+e1sZ5ipDzk7zFGhZ2k68PoQoFdsFOO3C513JmuEdavspts6Hkifsqh61TaE+A==", + "version": "17.2.8", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-17.2.8.tgz", + "integrity": "sha512-0cXzp1tGr7/6lJel102QiLA4NkaLCkQJj6VzwbwuvmuCDxPbpmbz7HC1tUteijKBtOcdXit1/MEoEU007To8Bw==", "cpu": [ "x64" ], @@ -4326,9 +4775,9 @@ } }, "node_modules/@nx/nx-freebsd-x64": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-17.1.3.tgz", - "integrity": "sha512-CRuVL5ZSLb+Gc8vwMUUe9Pl/1Z26YtXMKTahBMQh2dac63vzLgzqIV4c66aduUl1x2M0kGYBSIIRG9z0/BgWeg==", + "version": "17.2.8", + "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-17.2.8.tgz", + "integrity": "sha512-YFMgx5Qpp2btCgvaniDGdu7Ctj56bfFvbbaHQWmOeBPK1krNDp2mqp8HK6ZKOfEuDJGOYAp7HDtCLvdZKvJxzA==", "cpu": [ "x64" ], @@ -4342,9 +4791,9 @@ } }, "node_modules/@nx/nx-linux-arm-gnueabihf": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-17.1.3.tgz", - "integrity": "sha512-KDBmd5tSrg93g/oij/eGW4yeVNVK3DBIM4VYAS2vtkIgVOGoqcQ+SEIeMK3nMUJP9jGyblt3QNj5ZsJBtScwQw==", + "version": "17.2.8", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-17.2.8.tgz", + "integrity": "sha512-iN2my6MrhLRkVDtdivQHugK8YmR7URo1wU9UDuHQ55z3tEcny7LV3W9NSsY9UYPK/FrxdDfevj0r2hgSSdhnzA==", "cpu": [ "arm" ], @@ -4358,9 +4807,9 @@ } }, "node_modules/@nx/nx-linux-arm64-gnu": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-17.1.3.tgz", - "integrity": "sha512-W2tNL/7sIwoQKLmuy68Usd6TZzIZvxZt4UE30kDwGc2RSap6RCHAvDbzSxtW+L4+deC9UxX0Tty0VuW+J8FjSg==", + "version": "17.2.8", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-17.2.8.tgz", + "integrity": "sha512-Iy8BjoW6mOKrSMiTGujUcNdv+xSM1DALTH6y3iLvNDkGbjGK1Re6QNnJAzqcXyDpv32Q4Fc57PmuexyysZxIGg==", "cpu": [ "arm64" ], @@ -4374,9 +4823,9 @@ } }, "node_modules/@nx/nx-linux-arm64-musl": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-17.1.3.tgz", - "integrity": "sha512-Oto3gkLd7yweuVUCsSHwm4JkAIbcxpPJP0ycRHI/PRHPMIOPiMX8r651QM1amMyKAbJtAe047nyb9Sh1X0FA4A==", + "version": "17.2.8", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-17.2.8.tgz", + "integrity": "sha512-9wkAxWzknjpzdofL1xjtU6qPFF1PHlvKCZI3hgEYJDo4mQiatGI+7Ttko+lx/ZMP6v4+Umjtgq7+qWrApeKamQ==", "cpu": [ "arm64" ], @@ -4390,9 +4839,9 @@ } }, "node_modules/@nx/nx-linux-x64-gnu": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-17.1.3.tgz", - "integrity": "sha512-pJS994sa5PBPFak93RydTB9KdEmiVb3rgiSB7PDBegphERbzHEB77B7G8M5TZ62dGlMdplIEKmdhY5XNqeAf9A==", + "version": "17.2.8", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-17.2.8.tgz", + "integrity": "sha512-sjG1bwGsjLxToasZ3lShildFsF0eyeGu+pOQZIp9+gjFbeIkd19cTlCnHrOV9hoF364GuKSXQyUlwtFYFR4VTQ==", "cpu": [ "x64" ], @@ -4406,9 +4855,9 @@ } }, "node_modules/@nx/nx-linux-x64-musl": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-17.1.3.tgz", - "integrity": "sha512-4Hcx5Fg/88jV+bcTr6P0dM4unXNvKgrGJe3oK9/sgEhiW6pD2UAFjv16CCSRcWhDUAzUDqcwnD2fgg+vnAJG6g==", + "version": "17.2.8", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-17.2.8.tgz", + "integrity": "sha512-QiakXZ1xBCIptmkGEouLHQbcM4klQkcr+kEaz2PlNwy/sW3gH1b/1c0Ed5J1AN9xgQxWspriAONpScYBRgxdhA==", "cpu": [ "x64" ], @@ -4422,9 +4871,9 @@ } }, "node_modules/@nx/nx-win32-arm64-msvc": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-17.1.3.tgz", - "integrity": "sha512-dUasEuskmDxUL36XA0GZqSb9233suE4wKhxrMobyFBzHUZ2tq/unzOpPjYfqDBie4QIvF8tEpAjQsLds8LWgbw==", + "version": "17.2.8", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-17.2.8.tgz", + "integrity": "sha512-XBWUY/F/GU3vKN9CAxeI15gM4kr3GOBqnzFZzoZC4qJt2hKSSUEWsMgeZtsMgeqEClbi4ZyCCkY7YJgU32WUGA==", "cpu": [ "arm64" ], @@ -4438,9 +4887,9 @@ } }, "node_modules/@nx/nx-win32-x64-msvc": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-17.1.3.tgz", - "integrity": "sha512-eTuTpBHFvA5NFJh/iosmqCL4JOAjDrwXLSMgfKrZKjiApHMG1T/5Hb+PrsNpt+WnGp94ur7c4Dtx4xD5vlpAEw==", + "version": "17.2.8", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-17.2.8.tgz", + "integrity": "sha512-HTqDv+JThlLzbcEm/3f+LbS5/wYQWzb5YDXbP1wi7nlCTihNZOLNqGOkEmwlrR5tAdNHPRpHSmkYg4305W0CtA==", "cpu": [ "x64" ], @@ -4487,14 +4936,183 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.6.tgz", + "integrity": "sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.6.tgz", + "integrity": "sha512-T14aNLpqJ5wzKNf5jEDpv5zgyIqcpn1MlwCrUXLrwoADr2RkWA0vOWP4XxbO9aiO3dvMCQICZdKeDrFl7UMClw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.6.tgz", + "integrity": "sha512-CqNNAyhRkTbo8VVZ5R85X73H3R5NX9ONnKbXuHisGWC0qRbTTxnF1U4V9NafzJbgGM0sHZpdO83pLPzq8uOZFw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.6.tgz", + "integrity": "sha512-zRDtdJuRvA1dc9Mp6BWYqAsU5oeLixdfUvkTHuiYOHwqYuQ4YgSmi6+/lPvSsqc/I0Omw3DdICx4Tfacdzmhog==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.6.tgz", + "integrity": "sha512-oNk8YXDDnNyG4qlNb6is1ojTOGL/tRhbbKeE/YuccItzerEZT68Z9gHrY3ROh7axDc974+zYAPxK5SH0j/G+QQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.6.tgz", + "integrity": "sha512-Z3O60yxPtuCYobrtzjo0wlmvDdx2qZfeAWTyfOjEDqd08kthDKexLpV97KfAeUXPosENKd8uyJMRDfFMxcYkDQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.6.tgz", + "integrity": "sha512-gpiG0qQJNdYEVad+1iAsGAbgAnZ8j07FapmnIAQgODKcOTjLEWM9sRb+MbQyVsYCnA0Im6M6QIq6ax7liws6eQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.6.tgz", + "integrity": "sha512-+uCOcvVmFUYvVDr27aiyun9WgZk0tXe7ThuzoUTAukZJOwS5MrGbmSlNOhx1j80GdpqbOty05XqSl5w4dQvcOA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.6.tgz", + "integrity": "sha512-HUNqM32dGzfBKuaDUBqFB7tP6VMN74eLZ33Q9Y1TBqRDn+qDonkAUyKWwF9BR9unV7QUzffLnz9GrnKvMqC/fw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.6.tgz", + "integrity": "sha512-ch7M+9Tr5R4FK40FHQk8VnML0Szi2KRujUgHXd/HjuH9ifH72GUmw6lStZBo3c3GB82vHa0ZoUfjfcM7JiiMrQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.6.tgz", + "integrity": "sha512-VD6qnR99dhmTQ1mJhIzXsRcTBvTjbfbGGwKAHcu+52cVl15AC/kplkhxzW/uT0Xl62Y/meBKDZvoJSJN+vTeGA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.6.tgz", + "integrity": "sha512-J9AFDq/xiRI58eR2NIDfyVmTYGyIZmRcvcAoJ48oDld/NTR8wyiPUu2X/v1navJ+N/FGg68LEbX3Ejd6l8B7MQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.6.tgz", + "integrity": "sha512-jqzNLhNDvIZOrt69Ce4UjGRpXJBzhUBzawMwnaDAwyHriki3XollsewxWzOzz+4yOFDkuJHtTsZFwMxhYJWmLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@schematics/angular": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-17.0.8.tgz", - "integrity": "sha512-1h5mwKFv1B/L5JWZ0mxnC4ms06iwnSi/w+GgRZPeM3P5BpuZuvAkFiClNnM55iLlQJXRQioPNLM3sOsz7spR6w==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-17.1.2.tgz", + "integrity": "sha512-1GlH0POaN7hVDF1sAm90E5SvAqnKK+PbD1oKSpug9l+1AUQ3vOamyGhEAaO+IxUqvNdgqZexxd5o9MyySTT2Zw==", "dev": true, "dependencies": { - "@angular-devkit/core": "17.0.8", - "@angular-devkit/schematics": "17.0.8", + "@angular-devkit/core": "17.1.2", + "@angular-devkit/schematics": "17.1.2", "jsonc-parser": "3.2.0" }, "engines": { @@ -4525,9 +5143,9 @@ "dev": true }, "node_modules/@sigstore/bundle": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.1.0.tgz", - "integrity": "sha512-89uOo6yh/oxaU8AeOUnVrTdVMcGk9Q1hJa7Hkvalc6G3Z3CupWk4Xe9djSgJm9fMkH69s0P0cVHUoKSOemLdng==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.1.1.tgz", + "integrity": "sha512-v3/iS+1nufZdKQ5iAlQKcCsoh0jffQyABvYIxKsZQFWc4ubuGjwZklFHpDgV6O6T7vvV78SW5NHI91HFKEcxKg==", "dev": true, "dependencies": { "@sigstore/protobuf-specs": "^0.2.1" @@ -4536,6 +5154,15 @@ "node": "^16.14.0 || >=18.0.0" } }, + "node_modules/@sigstore/core": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-0.2.0.tgz", + "integrity": "sha512-THobAPPZR9pDH2CAvDLpkrYedt7BlZnsyxDe+Isq4ZmGfPy5juOFZq487vCU2EgKD7aHSiTfE/i7sN7aEdzQnA==", + "dev": true, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, "node_modules/@sigstore/protobuf-specs": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.2.1.tgz", @@ -4546,12 +5173,13 @@ } }, "node_modules/@sigstore/sign": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-2.2.0.tgz", - "integrity": "sha512-AAbmnEHDQv6CSfrWA5wXslGtzLPtAtHZleKOgxdQYvx/s76Fk6T6ZVt7w2IGV9j1UrFeBocTTQxaXG2oRrDhYA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-2.2.1.tgz", + "integrity": "sha512-U5sKQEj+faE1MsnLou1f4DQQHeFZay+V9s9768lw48J4pKykPj34rWyI1lsMOGJ3Mae47Ye6q3HAJvgXO21rkQ==", "dev": true, "dependencies": { - "@sigstore/bundle": "^2.1.0", + "@sigstore/bundle": "^2.1.1", + "@sigstore/core": "^0.2.0", "@sigstore/protobuf-specs": "^0.2.1", "make-fetch-happen": "^13.0.0" }, @@ -4560,13 +5188,27 @@ } }, "node_modules/@sigstore/tuf": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.2.0.tgz", - "integrity": "sha512-KKATZ5orWfqd9ZG6MN8PtCIx4eevWSuGRKQvofnWXRpyMyUEpmrzg5M5BrCpjM+NfZ0RbNGOh5tCz/P2uoRqOA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.3.0.tgz", + "integrity": "sha512-S98jo9cpJwO1mtQ+2zY7bOdcYyfVYCUaofCG6wWRzk3pxKHVAkSfshkfecto2+LKsx7Ovtqbgb2LS8zTRhxJ9Q==", "dev": true, "dependencies": { "@sigstore/protobuf-specs": "^0.2.1", - "tuf-js": "^2.1.0" + "tuf-js": "^2.2.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/verify": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-0.1.0.tgz", + "integrity": "sha512-2UzMNYAa/uaz11NhvgRnIQf4gpLTJ59bhb8ESXaoSS5sxedfS+eLak8bsdMc+qpNQfITUTFoSKFx5h8umlRRiA==", + "dev": true, + "dependencies": { + "@sigstore/bundle": "^2.1.1", + "@sigstore/core": "^0.2.0", + "@sigstore/protobuf-specs": "^0.2.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" @@ -4596,12 +5238,6 @@ "@sinonjs/commons": "^3.0.0" } }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", - "dev": true - }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -4698,7 +5334,6 @@ "version": "7.6.7", "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.7.tgz", "integrity": "sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==", - "dev": true, "dependencies": { "@babel/types": "^7.0.0" } @@ -4707,7 +5342,6 @@ "version": "7.4.4", "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" @@ -4717,7 +5351,6 @@ "version": "7.20.4", "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.4.tgz", "integrity": "sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==", - "dev": true, "dependencies": { "@babel/types": "^7.20.7" } @@ -4760,21 +5393,6 @@ "@types/node": "*" } }, - "node_modules/@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "dev": true - }, - "node_modules/@types/cors": { - "version": "2.8.17", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", - "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/eslint": { "version": "8.44.9", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.9.tgz", @@ -4814,9 +5432,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.41", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", - "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", + "version": "4.17.42", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.42.tgz", + "integrity": "sha512-ckM3jm2bf/MfB3+spLPWYPUH573plBFwpOhqQ2WottxYV85j1HQFlxmnTq57X1yHY9awZPig06hL/cLMgNWHIQ==", "dev": true, "dependencies": { "@types/node": "*", @@ -4916,9 +5534,9 @@ } }, "node_modules/@types/node-forge": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.10.tgz", - "integrity": "sha512-y6PJDYN4xYBxwd22l+OVH35N+1fCYWiuC3aiP2SlXVE6Lo7SS+rSx9r89hLxrP4pn6n1lBGhHJ12pj3F3Mpttw==", + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", "dev": true, "dependencies": { "@types/node": "*" @@ -5156,13 +5774,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.1.tgz", - "integrity": "sha512-A2qPlgpxx2v//3meMqQyB1qqTg1h1dJvzca7TugM3Yc2USDY+fsRBiojAEo92HO7f5hW5mjAUF6qobOPzlBCBQ==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.19.0.tgz", + "integrity": "sha512-mcvS6WSWbjiSxKCwBcXtOM5pRkPQ6kcDds/juxcy/727IQr3xMEcwr/YLHW2A2+Fp5ql6khjbKBzOyjuPqGi/w==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.13.1", - "@typescript-eslint/utils": "6.13.1", + "@typescript-eslint/typescript-estree": "6.19.0", + "@typescript-eslint/utils": "6.19.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -5182,10 +5800,27 @@ } } }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.0.tgz", + "integrity": "sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz", - "integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.0.tgz", + "integrity": "sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -5196,16 +5831,17 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz", - "integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.0.tgz", + "integrity": "sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", + "minimatch": "9.0.3", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, @@ -5222,13 +5858,38 @@ } } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz", - "integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==", + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.19.0.tgz", + "integrity": "sha512-QR41YXySiuN++/dC9UArYOg4X86OAYP83OWTewpVx5ct1IZhjjgTLocj7QNxGhWoTqknsgpl7L+hGygCO+sdYw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.1", + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/typescript-estree": "6.19.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz", + "integrity": "sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.19.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -5239,6 +5900,30 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/types": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", @@ -5304,105 +5989,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/utils": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.1.tgz", - "integrity": "sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.13.1", - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/typescript-estree": "6.13.1", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz", - "integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz", - "integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz", - "integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz", - "integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.13.1", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/visitor-keys": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", @@ -5427,15 +6013,15 @@ "dev": true }, "node_modules/@vitejs/plugin-basic-ssl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.0.1.tgz", - "integrity": "sha512-pcub+YbFtFhaGRTo1832FQHQSHvMrlb43974e2eS8EKleR3p1cDdkJFPci1UhwkEf1J9Bz+wKBSzqpKp7nNj2A==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.0.2.tgz", + "integrity": "sha512-DKHKVtpI+eA5fvObVgQ3QtTGU70CcCnedalzqmGSR050AzKZMdUzgC8KmlOneHWH8dF2hJ3wkC9+8FDVAaDRCw==", "dev": true, "engines": { "node": ">=14.6.0" }, "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0" + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" } }, "node_modules/@webassemblyjs/ast": { @@ -5930,9 +6516,9 @@ } }, "node_modules/array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true }, "node_modules/array-union": { @@ -5950,15 +6536,6 @@ "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "dev": true }, - "node_modules/async-each-series": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/async-each-series/-/async-each-series-0.1.1.tgz", - "integrity": "sha512-p4jj6Fws4Iy2m0iCmI2am2ZNZCgbdgE+P8F/8csmn2vx7ixXrO2zGcuNsD46X5uZSVecmkEy/M06X2vG8KD6dQ==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -6012,12 +6589,14 @@ } }, "node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", + "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", "dev": true, "dependencies": { - "follow-redirects": "^1.14.0" + "follow-redirects": "^1.15.4", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "node_modules/axobject-query": { @@ -6169,13 +6748,13 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.7.tgz", - "integrity": "sha512-LidDk/tEGDfuHW2DWh/Hgo4rmnw3cduK6ZkOI1NPFceSK3n/yAGeOsNT7FLnSGHkXj3RHGSEVkN3FsCTY6w2CQ==", + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.8.tgz", + "integrity": "sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg==", "dev": true, "dependencies": { "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.4.4", + "@babel/helper-define-polyfill-provider": "^0.5.0", "semver": "^6.3.1" }, "peerDependencies": { @@ -6204,13 +6783,29 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.4.tgz", - "integrity": "sha512-S/x2iOCvDaCASLYsOOgWOq4bCfKYVqvO/uxjkaYyZ3rVsVE3CeAI/c84NpyuBBymEgNvHgjEot3a9/Z/kXvqsg==", + "node_modules/babel-plugin-polyfill-corejs3/node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.4.tgz", + "integrity": "sha512-QcJMILQCu2jm5TFPGA3lCpJJTeEP+mqeXooG/NZbg/h5FTFi6V0+99ahlRsW8/kRLyb24LZVCCiclDedhLKcBA==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.4" + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz", + "integrity": "sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.5.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -6281,15 +6876,6 @@ } ] }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "dev": true, - "engines": { - "node": "^4.5.0 || >= 5.9" - } - }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -6348,6 +6934,15 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/body-parser/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -6357,57 +6952,18 @@ "ms": "2.0.0" } }, - "node_modules/body-parser/node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/body-parser/node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dev": true, - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/bonjour-service": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", - "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", "dev": true, "dependencies": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", "fast-deep-equal": "^3.1.3", "multicast-dns": "^7.2.5" } @@ -6457,218 +7013,6 @@ "node": ">=8" } }, - "node_modules/browser-sync": { - "version": "2.29.3", - "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.29.3.tgz", - "integrity": "sha512-NiM38O6XU84+MN+gzspVmXV2fTOoe+jBqIBx3IBdhZrdeURr6ZgznJr/p+hQ+KzkKEiGH/GcC4SQFSL0jV49bg==", - "dev": true, - "dependencies": { - "browser-sync-client": "^2.29.3", - "browser-sync-ui": "^2.29.3", - "bs-recipes": "1.3.4", - "chalk": "4.1.2", - "chokidar": "^3.5.1", - "connect": "3.6.6", - "connect-history-api-fallback": "^1", - "dev-ip": "^1.0.1", - "easy-extender": "^2.3.4", - "eazy-logger": "^4.0.1", - "etag": "^1.8.1", - "fresh": "^0.5.2", - "fs-extra": "3.0.1", - "http-proxy": "^1.18.1", - "immutable": "^3", - "localtunnel": "^2.0.1", - "micromatch": "^4.0.2", - "opn": "5.3.0", - "portscanner": "2.2.0", - "raw-body": "^2.3.2", - "resp-modifier": "6.0.2", - "rx": "4.1.0", - "send": "0.16.2", - "serve-index": "1.9.1", - "serve-static": "1.13.2", - "server-destroy": "1.0.1", - "socket.io": "^4.4.1", - "ua-parser-js": "^1.0.33", - "yargs": "^17.3.1" - }, - "bin": { - "browser-sync": "dist/bin.js" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/browser-sync-client": { - "version": "2.29.3", - "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.29.3.tgz", - "integrity": "sha512-4tK5JKCl7v/3aLbmCBMzpufiYLsB1+UI+7tUXCCp5qF0AllHy/jAqYu6k7hUF3hYtlClKpxExWaR+rH+ny07wQ==", - "dev": true, - "dependencies": { - "etag": "1.8.1", - "fresh": "0.5.2", - "mitt": "^1.1.3" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/browser-sync-ui": { - "version": "2.29.3", - "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.29.3.tgz", - "integrity": "sha512-kBYOIQjU/D/3kYtUIJtj82e797Egk1FB2broqItkr3i4eF1qiHbFCG6srksu9gWhfmuM/TNG76jMfzAdxEPakg==", - "dev": true, - "dependencies": { - "async-each-series": "0.1.1", - "chalk": "4.1.2", - "connect-history-api-fallback": "^1", - "immutable": "^3", - "server-destroy": "1.0.1", - "socket.io-client": "^4.4.1", - "stream-throttle": "^0.1.3" - } - }, - "node_modules/browser-sync-ui/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/browser-sync-ui/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/browser-sync-ui/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/browser-sync-ui/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/browser-sync-ui/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-sync-ui/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-sync/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/browser-sync/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/browser-sync/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/browser-sync/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/browser-sync/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-sync/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/browserslist": { "version": "4.22.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", @@ -6712,12 +7056,6 @@ "node": ">= 6" } }, - "node_modules/bs-recipes": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/bs-recipes/-/bs-recipes-1.3.4.tgz", - "integrity": "sha512-BXvDkqhDNxXEjeGM8LFkSbR+jzmP/CYpCiVKYn+soB1dDldeU15EBNDkwVXndKuX35wnNUaPd0qSoQEAkmQtMw==", - "dev": true - }, "node_modules/bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -6767,18 +7105,18 @@ } }, "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", "dev": true, "engines": { "node": ">= 0.8" } }, "node_modules/cacache": { - "version": "18.0.1", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.1.tgz", - "integrity": "sha512-g4Uf2CFZPaxtJKre6qr4zqLDOOPU7bNVhWjlNhvzc51xaTOx2noMOLhfFkTAqwtrAZAKQUuDfyjitzilpA8WsQ==", + "version": "18.0.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.2.tgz", + "integrity": "sha512-r3NU8h/P+4lVUHfeRw1dtgQYar3DZMm4/cm2bZgOvrFC/su7budSOeqh52VJIC4U4iG1WWwV6vRW0znqBvxNuw==", "dev": true, "dependencies": { "@npmcli/fs": "^3.1.0", @@ -6830,9 +7168,9 @@ } }, "node_modules/cacache/node_modules/lru-cache": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", - "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "dev": true, "engines": { "node": "14 || >=16.14" @@ -7234,15 +7572,6 @@ "node": ">= 0.8.0" } }, - "node_modules/compression/node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/compression/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -7382,45 +7711,15 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/connect": { - "version": "3.6.6", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", - "integrity": "sha512-OO7axMmPpu/2XuX1+2Yrg0ddju31B6xLZMWkJ5rYBu4YRmRVlOjvlY6kw2FJKiAzyxGwnrDUAG4s1Pf0sbBMCQ==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "finalhandler": "1.1.0", - "parseurl": "~1.3.2", - "utils-merge": "1.0.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, "node_modules/connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", "dev": true, "engines": { "node": ">=0.8" } }, - "node_modules/connect/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/connect/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -7454,9 +7753,9 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, "node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "dev": true, "engines": { "node": ">= 0.6" @@ -7548,9 +7847,9 @@ } }, "node_modules/core-js-compat": { - "version": "3.34.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.34.0.tgz", - "integrity": "sha512-4ZIyeNbW/Cn1wkMMDy+mvrRUxrwFNjKwbhCfQpDd+eLgYipDqp8oGFGtLmhh18EDPKA0g3VUBYOxQGGwvWLVpA==", + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.35.1.tgz", + "integrity": "sha512-sftHa5qUJY3rs9Zht1WEnmkvXputCyDBczPnr7QDgL8n3qrF3CMXY4VPSYtOLLiOUJcah2WNXREd48iOl6mQIw==", "dev": true, "dependencies": { "browserslist": "^4.22.2" @@ -7566,19 +7865,6 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dev": true, - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/cosmiconfig": { "version": "8.3.6", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", @@ -8083,10 +8369,14 @@ } }, "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", - "dev": true + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } }, "node_modules/detect-libc": { "version": "2.0.2", @@ -8112,18 +8402,6 @@ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true }, - "node_modules/dev-ip": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dev-ip/-/dev-ip-1.0.1.tgz", - "integrity": "sha512-LmVkry/oDShEgSZPNgqCIp2/TlqtExeGmymru3uCELnfyjY11IzpAproLYs+1X88fXO6DBoYP3ul2Xo2yz2j6A==", - "dev": true, - "bin": { - "dev-ip": "lib/dev-ip.js" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -8154,12 +8432,6 @@ "node": ">=8" } }, - "node_modules/dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", - "dev": true - }, "node_modules/dns-packet": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", @@ -8285,100 +8557,6 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, - "node_modules/easy-extender": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/easy-extender/-/easy-extender-2.3.4.tgz", - "integrity": "sha512-8cAwm6md1YTiPpOvDULYJL4ZS6WfM5/cTeVVh4JsvyYZAoqlRVUpHL9Gr5Fy7HA6xcSZicUia3DeAgO3Us8E+Q==", - "dev": true, - "dependencies": { - "lodash": "^4.17.10" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/eazy-logger": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/eazy-logger/-/eazy-logger-4.0.1.tgz", - "integrity": "sha512-2GSFtnnC6U4IEKhEI7+PvdxrmjJ04mdsj3wHZTFiw0tUtG4HCWzTr13ZYTk8XOGnA1xQMaDljoBOYlk3D/MMSw==", - "dev": true, - "dependencies": { - "chalk": "4.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/eazy-logger/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eazy-logger/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eazy-logger/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eazy-logger/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/eazy-logger/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eazy-logger/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -8472,91 +8650,6 @@ "once": "^1.4.0" } }, - "node_modules/engine.io": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", - "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", - "dev": true, - "dependencies": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.11.0" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/engine.io-client": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", - "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", - "dev": true, - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.11.0", - "xmlhttprequest-ssl": "~2.0.0" - } - }, - "node_modules/engine.io-client/node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/engine.io-parser": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", - "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==", - "dev": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io/node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/enhanced-resolve": { "version": "5.15.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", @@ -8638,12 +8731,11 @@ "dev": true }, "node_modules/esbuild": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.5.tgz", - "integrity": "sha512-bUxalY7b1g8vNhQKdB24QDmHeY4V4tw/s6Ak5z+jJX9laP5MoQseTOMemAr0gxssjNcH0MCViG8ONI2kksvfFQ==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.11.tgz", + "integrity": "sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==", "dev": true, "hasInstallScript": true, - "optional": true, "bin": { "esbuild": "bin/esbuild" }, @@ -8651,34 +8743,35 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.19.5", - "@esbuild/android-arm64": "0.19.5", - "@esbuild/android-x64": "0.19.5", - "@esbuild/darwin-arm64": "0.19.5", - "@esbuild/darwin-x64": "0.19.5", - "@esbuild/freebsd-arm64": "0.19.5", - "@esbuild/freebsd-x64": "0.19.5", - "@esbuild/linux-arm": "0.19.5", - "@esbuild/linux-arm64": "0.19.5", - "@esbuild/linux-ia32": "0.19.5", - "@esbuild/linux-loong64": "0.19.5", - "@esbuild/linux-mips64el": "0.19.5", - "@esbuild/linux-ppc64": "0.19.5", - "@esbuild/linux-riscv64": "0.19.5", - "@esbuild/linux-s390x": "0.19.5", - "@esbuild/linux-x64": "0.19.5", - "@esbuild/netbsd-x64": "0.19.5", - "@esbuild/openbsd-x64": "0.19.5", - "@esbuild/sunos-x64": "0.19.5", - "@esbuild/win32-arm64": "0.19.5", - "@esbuild/win32-ia32": "0.19.5", - "@esbuild/win32-x64": "0.19.5" + "@esbuild/aix-ppc64": "0.19.11", + "@esbuild/android-arm": "0.19.11", + "@esbuild/android-arm64": "0.19.11", + "@esbuild/android-x64": "0.19.11", + "@esbuild/darwin-arm64": "0.19.11", + "@esbuild/darwin-x64": "0.19.11", + "@esbuild/freebsd-arm64": "0.19.11", + "@esbuild/freebsd-x64": "0.19.11", + "@esbuild/linux-arm": "0.19.11", + "@esbuild/linux-arm64": "0.19.11", + "@esbuild/linux-ia32": "0.19.11", + "@esbuild/linux-loong64": "0.19.11", + "@esbuild/linux-mips64el": "0.19.11", + "@esbuild/linux-ppc64": "0.19.11", + "@esbuild/linux-riscv64": "0.19.11", + "@esbuild/linux-s390x": "0.19.11", + "@esbuild/linux-x64": "0.19.11", + "@esbuild/netbsd-x64": "0.19.11", + "@esbuild/openbsd-x64": "0.19.11", + "@esbuild/sunos-x64": "0.19.11", + "@esbuild/win32-arm64": "0.19.11", + "@esbuild/win32-ia32": "0.19.11", + "@esbuild/win32-x64": "0.19.11" } }, "node_modules/esbuild-wasm": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.19.5.tgz", - "integrity": "sha512-7zmLLn2QCj93XfMmHtzrDJ1UBuOHB2CZz1ghoCEZiRajxjUvHsF40PnbzFIY/pmesqPRaEtEWii0uzsTbnAgrA==", + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.19.11.tgz", + "integrity": "sha512-MIhnpc1TxERUHomteO/ZZHp+kUawGEc03D/8vMHGzffLvbFLeDe6mwxqEZwlqBNY7SLWbyp6bBQAcCen8+wpjQ==", "dev": true, "bin": { "esbuild": "bin/esbuild" @@ -9111,12 +9204,6 @@ "node": ">= 0.6" } }, - "node_modules/eventemitter-asyncresource": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", - "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", - "dev": true - }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -9228,21 +9315,6 @@ "node": ">= 0.10.0" } }, - "node_modules/express/node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true - }, - "node_modules/express/node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -9252,106 +9324,12 @@ "ms": "2.0.0" } }, - "node_modules/express/node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/express/node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/express/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/express/node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/express/node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/express/node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dev": true, - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/express/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -9527,17 +9505,17 @@ } }, "node_modules/finalhandler": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", - "integrity": "sha512-ejnvM9ZXYzp6PUPUyQBMBf0Co5VX2gr5H2VQe2Ui2jWXNlxv+PYZo8wpAymJNJdLsG1R4p+M4aynF8KuoUEwRw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dev": true, "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.1", + "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.3.1", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", "unpipe": "~1.0.0" }, "engines": { @@ -9627,9 +9605,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", - "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", "dev": true, "funding": [ { @@ -9726,14 +9704,17 @@ "dev": true }, "node_modules/fs-extra": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", - "integrity": "sha512-V3Z3WZWVUYd8hoCL5xfXJCaHWYzmtwW5XWYSlLgERi8PWd8bx1kUHUk8L1BT57e49oKnDDD180mjfrHc1yA9rg==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dev": true, "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^3.0.0", - "universalify": "^0.1.0" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" } }, "node_modules/fs-minipass": { @@ -10041,9 +10022,9 @@ } }, "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", - "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "dev": true, "engines": { "node": "14 || >=16.14" @@ -10178,15 +10159,6 @@ "node": ">= 0.8" } }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/http-parser-js": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", @@ -10382,13 +10354,10 @@ } }, "node_modules/immutable": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", - "integrity": "sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", + "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==", + "dev": true }, "node_modules/import-fresh": { "version": "3.3.0", @@ -10490,12 +10459,12 @@ } }, "node_modules/inquirer": { - "version": "9.2.11", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.11.tgz", - "integrity": "sha512-B2LafrnnhbRzCWfAdOXisUzL89Kg8cVJlYmhqoi3flSiV/TveO+nsXwgKr9h9PIo+J1hz7nBSk6gegRIMBBf7g==", + "version": "9.2.12", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.12.tgz", + "integrity": "sha512-mg3Fh9g2zfuVWJn6lhST0O7x4n03k7G8Tx5nvikJkbq8/CK47WDVm+UznF0G6s5Zi0KcyUisr6DU8T67N5U+1Q==", "dev": true, "dependencies": { - "@ljharb/through": "^2.3.9", + "@ljharb/through": "^2.3.11", "ansi-escapes": "^4.3.2", "chalk": "^5.3.0", "cli-cursor": "^3.1.0", @@ -10645,15 +10614,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-number-like": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/is-number-like/-/is-number-like-1.0.8.tgz", - "integrity": "sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA==", - "dev": true, - "dependencies": { - "lodash.isfinite": "^3.3.2" - } - }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -12788,10 +12748,13 @@ "dev": true }, "node_modules/jsonfile": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", - "integrity": "sha512-oBko6ZHlubVB5mRFkur5vgYR1UyqX+S6Y/oCfLhqNdcc2fYFlDpIoNc7AfKS1KOGcnNAkvsr0grLck9ANM815w==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -12997,12 +12960,6 @@ } } }, - "node_modules/limiter": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", - "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==", - "dev": true - }, "node_modules/lines-and-columns": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz", @@ -13030,129 +12987,6 @@ "node": ">= 12.13.0" } }, - "node_modules/localtunnel": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/localtunnel/-/localtunnel-2.0.2.tgz", - "integrity": "sha512-n418Cn5ynvJd7m/N1d9WVJISLJF/ellZnfsLnx8WBWGzxv/ntNcFkJ1o6se5quUhCplfLGBNL5tYHiq5WF3Nug==", - "dev": true, - "dependencies": { - "axios": "0.21.4", - "debug": "4.3.2", - "openurl": "1.1.1", - "yargs": "17.1.1" - }, - "bin": { - "lt": "bin/lt.js" - }, - "engines": { - "node": ">=8.3.0" - } - }, - "node_modules/localtunnel/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/localtunnel/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/localtunnel/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/localtunnel/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/localtunnel/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/localtunnel/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/localtunnel/node_modules/yargs": { - "version": "17.1.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.1.1.tgz", - "integrity": "sha512-c2k48R0PwKIqKhPMWjeiF6y2xY/gPMUlro0sgxqXpbOIohWiLNXWslsootttv7E1e73QPAMQSg5FeySbVcpsPQ==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/localtunnel/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -13183,12 +13017,6 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, - "node_modules/lodash.isfinite": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz", - "integrity": "sha512-7FGG40uhC8Mm633uKW1r58aElFlBlxCrg9JfSi3P6aYiWmfiWF0PgMd86ZUsxE5GwWPdHoS2+48bwTh2VPkIQA==", - "dev": true - }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -13741,12 +13569,6 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "devOptional": true }, - "node_modules/mitt": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-1.2.0.tgz", - "integrity": "sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw==", - "dev": true - }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -13769,9 +13591,9 @@ } }, "node_modules/mrmime": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", - "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", "dev": true, "engines": { "node": ">=10" @@ -13938,9 +13760,9 @@ } }, "node_modules/ngx-ui-tour-core": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/ngx-ui-tour-core/-/ngx-ui-tour-core-12.0.0.tgz", - "integrity": "sha512-K2RnLMJHZvXjMAkdI1+N46tBLklnardf7Hm98bIAzc6w+5yyq53ttOJBFRk/fXSJZ7qFAfepPmyuInz6v6QYWg==", + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/ngx-ui-tour-core/-/ngx-ui-tour-core-12.0.1.tgz", + "integrity": "sha512-IkkSY8l+3huQ5D7cPZ7tnAtP7ijQ85ggsOIohekE/DHqXReX988S2xuXh5A0pU8bS+HLJw2wlC1/XohOknVumA==", "dependencies": { "tslib": "^2.0.0" }, @@ -13952,11 +13774,11 @@ } }, "node_modules/ngx-ui-tour-ng-bootstrap": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/ngx-ui-tour-ng-bootstrap/-/ngx-ui-tour-ng-bootstrap-14.0.1.tgz", - "integrity": "sha512-/mFHXmTPgpnSYNH0ejgZ3xW3/VXnR3qI24C6Crhqv8ftqxM/1C1fDtY6nJAjfgB+dEu5BIqLMdcT12BdF6UGNw==", + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/ngx-ui-tour-ng-bootstrap/-/ngx-ui-tour-ng-bootstrap-14.0.2.tgz", + "integrity": "sha512-2w9p5GHG/YDIwOQG+pct2s19P7hvtYbX73rYYV3FF86dXe00xPA7gGl2uSg48H3GjmSD0DeeKPzVWULwSBYNrw==", "dependencies": { - "ngx-ui-tour-core": "12.0.0", + "ngx-ui-tour-core": "12.0.1", "tslib": "^2.0.0" }, "peerDependencies": { @@ -14063,9 +13885,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.7.1.tgz", - "integrity": "sha512-wTSrZ+8lsRRa3I3H8Xr65dLWSgCvY2l4AOnaeKdPA9TB/WYMPaTcrzf3rXvFoVvjKNVnu0CcWSx54qq9GKRUYg==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", + "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", "dev": true, "optional": true, "bin": { @@ -14257,9 +14079,9 @@ } }, "node_modules/npm-packlist": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.1.tgz", - "integrity": "sha512-MQpL27ZrsJQ2kiAuQPpZb5LtJwydNRnI15QWXsf3WHERu4rzjRj6Zju/My2fov7tLuu3Gle/uoIX/DDZ3u4O4Q==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.2.tgz", + "integrity": "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==", "dev": true, "dependencies": { "ignore-walk": "^6.0.4" @@ -14344,13 +14166,13 @@ "dev": true }, "node_modules/nx": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/nx/-/nx-17.1.3.tgz", - "integrity": "sha512-6LYoTt01nS1d/dvvYtRs+pEAMQmUVsd2fr/a8+X1cDjWrb8wsf1O3DwlBTqKOXOazpS3eOr0Ukc9N1svbu7uXA==", + "version": "17.2.8", + "resolved": "https://registry.npmjs.org/nx/-/nx-17.2.8.tgz", + "integrity": "sha512-rM5zXbuXLEuqQqcjVjClyvHwRJwt+NVImR2A6KFNG40Z60HP6X12wAxxeLHF5kXXTDRU0PFhf/yACibrpbPrAw==", "dev": true, "hasInstallScript": true, "dependencies": { - "@nrwl/tao": "17.1.3", + "@nrwl/tao": "17.2.8", "@yarnpkg/lockfile": "^1.1.0", "@yarnpkg/parsers": "3.0.0-rc.46", "@zkochan/js-yaml": "0.0.6", @@ -14382,7 +14204,6 @@ "tmp": "~0.2.1", "tsconfig-paths": "^4.1.2", "tslib": "^2.3.0", - "v8-compile-cache": "2.3.0", "yargs": "^17.6.2", "yargs-parser": "21.1.1" }, @@ -14391,16 +14212,16 @@ "nx-cloud": "bin/nx-cloud.js" }, "optionalDependencies": { - "@nx/nx-darwin-arm64": "17.1.3", - "@nx/nx-darwin-x64": "17.1.3", - "@nx/nx-freebsd-x64": "17.1.3", - "@nx/nx-linux-arm-gnueabihf": "17.1.3", - "@nx/nx-linux-arm64-gnu": "17.1.3", - "@nx/nx-linux-arm64-musl": "17.1.3", - "@nx/nx-linux-x64-gnu": "17.1.3", - "@nx/nx-linux-x64-musl": "17.1.3", - "@nx/nx-win32-arm64-msvc": "17.1.3", - "@nx/nx-win32-x64-msvc": "17.1.3" + "@nx/nx-darwin-arm64": "17.2.8", + "@nx/nx-darwin-x64": "17.2.8", + "@nx/nx-freebsd-x64": "17.2.8", + "@nx/nx-linux-arm-gnueabihf": "17.2.8", + "@nx/nx-linux-arm64-gnu": "17.2.8", + "@nx/nx-linux-arm64-musl": "17.2.8", + "@nx/nx-linux-x64-gnu": "17.2.8", + "@nx/nx-linux-x64-musl": "17.2.8", + "@nx/nx-win32-arm64-msvc": "17.2.8", + "@nx/nx-win32-x64-msvc": "17.2.8" }, "peerDependencies": { "@swc-node/register": "^1.6.7", @@ -14436,17 +14257,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/nx/node_modules/axios": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", - "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", - "dev": true, - "dependencies": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, "node_modules/nx/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -14496,20 +14306,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/nx/node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, "node_modules/nx/node_modules/glob": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", @@ -14548,18 +14344,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/nx/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/nx/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -14611,15 +14395,6 @@ "node": ">=8" } }, - "node_modules/nx/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/nx/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -14630,7 +14405,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "devOptional": true, + "optional": true, "engines": { "node": ">=0.10.0" } @@ -14660,9 +14435,9 @@ "dev": true }, "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, "dependencies": { "ee-first": "1.1.1" @@ -14721,33 +14496,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/openurl": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/openurl/-/openurl-1.1.1.tgz", - "integrity": "sha512-d/gTkTb1i1GKz5k3XE3XFV/PxQ1k45zDqGP2OA7YhgsaLoqm6qRvARAZOFer1fcXritWlGBRCu/UgeS4HAnXAA==", - "dev": true - }, - "node_modules/opn": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", - "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", - "dev": true, - "dependencies": { - "is-wsl": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/opn/node_modules/is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -14968,9 +14716,9 @@ } }, "node_modules/pacote": { - "version": "17.0.4", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-17.0.4.tgz", - "integrity": "sha512-eGdLHrV/g5b5MtD5cTPyss+JxOlaOloSMG3UwPMAvL8ywaLJ6beONPF40K4KKl/UI6q5hTKCJq5rCu8tkF+7Dg==", + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-17.0.5.tgz", + "integrity": "sha512-TAE0m20zSDMnchPja9vtQjri19X3pZIyRpm2TJVeI+yU42leJBBDTRYhOcWFsPhaMxf+3iwQkFiKz16G9AEeeA==", "dev": true, "dependencies": { "@npmcli/git": "^5.0.0", @@ -15200,18 +14948,6 @@ "node": ">=8" } }, - "node_modules/patch-package/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/patch-package/node_modules/open": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", @@ -15273,15 +15009,6 @@ "node": ">=0.6.0" } }, - "node_modules/patch-package/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -15332,9 +15059,9 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", - "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "dev": true, "engines": { "node": "14 || >=16.14" @@ -15413,12 +15140,11 @@ } }, "node_modules/piscina": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.1.0.tgz", - "integrity": "sha512-sjbLMi3sokkie+qmtZpkfMCUJTpbxJm/wvaPzU28vmYSsTSW8xk9JcFUsbqGJdtPpIQ9tuj+iDcTtgZjwnOSig==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.2.1.tgz", + "integrity": "sha512-LShp0+lrO+WIzB9LXO+ZmO4zGHxtTJNZhEO56H9SSu+JPaUQb6oLcTCzWi5IL2DS8/vIkCE88ElahuSSw4TAkA==", "dev": true, "dependencies": { - "eventemitter-asyncresource": "^1.0.0", "hdr-histogram-js": "^2.0.1", "hdr-histogram-percentiles-obj": "^3.0.0" }, @@ -15567,33 +15293,10 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/portscanner": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.2.0.tgz", - "integrity": "sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw==", - "dev": true, - "dependencies": { - "async": "^2.6.0", - "is-number-like": "^1.0.3" - }, - "engines": { - "node": ">=0.4", - "npm": ">=1.0.0" - } - }, - "node_modules/portscanner/node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } - }, "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "version": "8.4.33", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", + "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", "dev": true, "funding": [ { @@ -15610,7 +15313,7 @@ } ], "dependencies": { - "nanoid": "^3.3.6", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -15619,14 +15322,14 @@ } }, "node_modules/postcss-loader": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.3.tgz", - "integrity": "sha512-YgO/yhtevGO/vJePCQmTxiaEwER94LABZN0ZMT4A0vsak9TpO+RvKRs7EmJ8peIlB9xfXCsS7M8LjqncsUZ5HA==", + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.4.tgz", + "integrity": "sha512-iW5WTTBSC5BfsBJ9daFMPVrLT36MrNiC6fqOZTTaHjBNX6Pfd5p+hSBqe/fEeNd7pc13QiAyGt7VdGMw4eRC4A==", "dev": true, "dependencies": { - "cosmiconfig": "^8.2.0", - "jiti": "^1.18.2", - "semver": "^7.3.8" + "cosmiconfig": "^8.3.5", + "jiti": "^1.20.0", + "semver": "^7.5.4" }, "engines": { "node": ">= 14.15.0" @@ -15937,9 +15640,9 @@ } }, "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dev": true, "dependencies": { "bytes": "3.1.2", @@ -15951,6 +15654,15 @@ "node": ">= 0.8" } }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", @@ -16276,34 +15988,6 @@ "node": ">=10" } }, - "node_modules/resp-modifier": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/resp-modifier/-/resp-modifier-6.0.2.tgz", - "integrity": "sha512-U1+0kWC/+4ncRFYqQWTx/3qkfE6a4B/h3XXgmXypfa0SPZ3t7cbbaFk297PjQS/yov24R18h6OZe6iZwj3NSLw==", - "dev": true, - "dependencies": { - "debug": "^2.2.0", - "minimatch": "^3.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/resp-modifier/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/resp-modifier/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -16351,18 +16035,34 @@ } }, "node_modules/rollup": { - "version": "3.29.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", - "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.6.tgz", + "integrity": "sha512-05lzkCS2uASX0CiLFybYfVkwNbKZG5NFQ6Go0VWyogFTXXbR039UVsegViTntkk4OglHBdF54ccApXRRuXRbsg==", "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, "bin": { "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=14.18.0", + "node": ">=18.0.0", "npm": ">=8.0.0" }, "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.9.6", + "@rollup/rollup-android-arm64": "4.9.6", + "@rollup/rollup-darwin-arm64": "4.9.6", + "@rollup/rollup-darwin-x64": "4.9.6", + "@rollup/rollup-linux-arm-gnueabihf": "4.9.6", + "@rollup/rollup-linux-arm64-gnu": "4.9.6", + "@rollup/rollup-linux-arm64-musl": "4.9.6", + "@rollup/rollup-linux-riscv64-gnu": "4.9.6", + "@rollup/rollup-linux-x64-gnu": "4.9.6", + "@rollup/rollup-linux-x64-musl": "4.9.6", + "@rollup/rollup-win32-arm64-msvc": "4.9.6", + "@rollup/rollup-win32-ia32-msvc": "4.9.6", + "@rollup/rollup-win32-x64-msvc": "4.9.6", "fsevents": "~2.3.2" } }, @@ -16397,12 +16097,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/rx": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", - "integrity": "sha512-CiaiuN6gapkdl+cZUr67W6I8jquN4lkak3vtIsIWCl4XIPP8ffsoyN6/+PuGXnQy8Cu8W2y9Xxh31Rq4M6wUug==", - "dev": true - }, "node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -16438,9 +16132,9 @@ "dev": true }, "node_modules/sass": { - "version": "1.69.5", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.69.5.tgz", - "integrity": "sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==", + "version": "1.69.7", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.69.7.tgz", + "integrity": "sha512-rzj2soDeZ8wtE2egyLXgOOHQvaC2iosZrkF6v3EUG+tBwEvhqUCzm0VP3k9gHF9LXbSrRhT5SksoI56Iw8NPnQ==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -16455,9 +16149,9 @@ } }, "node_modules/sass-loader": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.3.2.tgz", - "integrity": "sha512-CQbKl57kdEv+KDLquhC+gE3pXt74LEAzm+tzywcA0/aHZuub8wTErbjAoNI57rPUWRYRNC5WUnNl8eGJNbDdwg==", + "version": "13.3.3", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.3.3.tgz", + "integrity": "sha512-mt5YN2F1MOZr3d/wBRcZxeFgwgkH44wVc2zohO2YF6JiOMkiXe4BYRZpSu2sO1g71mo/j16txzUhsKZlqjVGzA==", "dev": true, "dependencies": { "neo-async": "^2.6.2" @@ -16491,12 +16185,6 @@ } } }, - "node_modules/sass/node_modules/immutable": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", - "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==", - "dev": true - }, "node_modules/sax": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", @@ -16585,24 +16273,24 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dev": true, "dependencies": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" }, "engines": { "node": ">= 0.8.0" @@ -16617,66 +16305,18 @@ "ms": "2.0.0" } }, - "node_modules/send/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/send/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", - "dev": true, - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/send/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "dev": true - }, - "node_modules/send/node_modules/mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", - "dev": true, - "bin": { - "mime": "cli.js" - } - }, - "node_modules/send/node_modules/ms": { + "node_modules/send/node_modules/debug/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/send/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/send/node_modules/statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/serialize-javascript": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", @@ -16765,26 +16405,20 @@ } }, "node_modules/serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dev": true, "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "parseurl": "~1.3.2", - "send": "0.16.2" + "parseurl": "~1.3.3", + "send": "0.18.0" }, "engines": { "node": ">= 0.8.0" } }, - "node_modules/server-destroy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", - "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", - "dev": true - }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -16875,15 +16509,17 @@ "devOptional": true }, "node_modules/sigstore": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-2.1.0.tgz", - "integrity": "sha512-kPIj+ZLkyI3QaM0qX8V/nSsweYND3W448pwkDgS6CQ74MfhEkIR8ToK5Iyx46KJYRjseVcD3Rp9zAmUAj6ZjPw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-2.2.0.tgz", + "integrity": "sha512-fcU9clHwEss2/M/11FFM8Jwc4PjBgbhXoNskoK5guoK0qGQBSeUbQZRJ+B2fDFIvhyf0gqCaPrel9mszbhAxug==", "dev": true, "dependencies": { - "@sigstore/bundle": "^2.1.0", + "@sigstore/bundle": "^2.1.1", + "@sigstore/core": "^0.2.0", "@sigstore/protobuf-specs": "^0.2.1", - "@sigstore/sign": "^2.1.0", - "@sigstore/tuf": "^2.1.0" + "@sigstore/sign": "^2.2.1", + "@sigstore/tuf": "^2.3.0", + "@sigstore/verify": "^0.1.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" @@ -16945,82 +16581,6 @@ "npm": ">= 3.0.0" } }, - "node_modules/socket.io": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.2.tgz", - "integrity": "sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==", - "dev": true, - "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.3.2", - "engine.io": "~6.5.2", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", - "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", - "dev": true, - "dependencies": { - "ws": "~8.11.0" - } - }, - "node_modules/socket.io-adapter/node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/socket.io-client": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.2.tgz", - "integrity": "sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==", - "dev": true, - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.2", - "engine.io-client": "~6.5.2", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "dev": true, - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -17088,17 +16648,16 @@ } }, "node_modules/source-map-loader": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.1.tgz", - "integrity": "sha512-oqXpzDIByKONVY8g1NUPOTQhe0UTU5bWUl32GSkqK2LjJj0HmwTMVKxcUip0RgAYhY1mqgOxjbQM48a0mmeNfA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-5.0.0.tgz", + "integrity": "sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==", "dev": true, "dependencies": { - "abab": "^2.0.6", "iconv-lite": "^0.6.3", "source-map-js": "^1.0.2" }, "engines": { - "node": ">= 14.15.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", @@ -17156,9 +16715,9 @@ } }, "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.4.0.tgz", + "integrity": "sha512-hcjppoJ68fhxA/cjbN4T8N6uCUejN8yFw69ttpqtBeCbF3u13n7mb31NB9jKwGTTWWnt9IbRA/mf1FprYS8wfw==", "dev": true }, "node_modules/spdx-expression-parse": { @@ -17247,28 +16806,12 @@ } }, "node_modules/statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha512-wuTCPGlJONk/a1kqZ4fQM2+908lC7fa7nPYpTC1EhnvqLX/IICbeP1OZGDtA374trpSq68YubKUMo8oRhN46yg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true, "engines": { - "node": ">= 0.6" - } - }, - "node_modules/stream-throttle": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/stream-throttle/-/stream-throttle-0.1.3.tgz", - "integrity": "sha512-889+B9vN9dq7/vLbGyuHeZ6/ctf5sNuGWsDy89uNxkFTAgzy0eK7+w5fL3KLNRTkLle7EgZGvHUphZW0Q26MnQ==", - "dev": true, - "dependencies": { - "commander": "^2.2.0", - "limiter": "^1.0.5" - }, - "bin": { - "throttleproxy": "bin/throttleproxy.js" - }, - "engines": { - "node": ">= 0.10.0" + "node": ">= 0.8" } }, "node_modules/string_decoder": { @@ -17512,9 +17055,9 @@ "devOptional": true }, "node_modules/terser": { - "version": "5.24.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.24.0.tgz", - "integrity": "sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==", + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.26.0.tgz", + "integrity": "sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==", "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -17900,9 +17443,9 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/tuf-js": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.1.0.tgz", - "integrity": "sha512-eD7YPPjVlMzdggrOeE8zwoegUaG/rt6Bt3jwoQPunRiNVzgcCE009UDFJKJjG+Gk9wFu6W/Vi+P5d/5QpdD9jA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.0.tgz", + "integrity": "sha512-ZSDngmP1z6zw+FIkIBjvOp/II/mIub/O7Pp12j1WNsiCpg5R5wAc//i555bBQsE44O94btLt0xM/Zr2LQjwdCg==", "dev": true, "dependencies": { "@tufjs/models": "2.0.0", @@ -17977,39 +17520,16 @@ "node": ">=14.17" } }, - "node_modules/ua-parser-js": { - "version": "1.0.37", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", - "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - }, - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" - } - ], - "engines": { - "node": "*" - } - }, "node_modules/undici": { - "version": "5.27.2", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.27.2.tgz", - "integrity": "sha512-iS857PdOEy/y3wlM3yRp+6SNQQ6xU0mmZcwRSriqk+et/cwWAtwmIGf6WkoDN2EK/AMdCO/dfXzIwi+rFMrjjQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.2.1.tgz", + "integrity": "sha512-7Wa9thEM6/LMnnKtxJHlc8SrTlDmxqJecgz1iy8KlsN0/iskQXOQCuPkrZLXbElPaSw5slFFyKIKXyJ3UtbApw==", "dev": true, "dependencies": { "@fastify/busboy": "^2.0.0" }, "engines": { - "node": ">=14.0" + "node": ">=18.0" } }, "node_modules/undici-types": { @@ -18083,12 +17603,12 @@ } }, "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { - "node": ">= 4.0.0" + "node": ">= 10.0.0" } }, "node_modules/unpipe": { @@ -18175,12 +17695,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -18239,29 +17753,29 @@ } }, "node_modules/vite": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.1.tgz", - "integrity": "sha512-AXXFaAJ8yebyqzoNB9fu2pHoo/nWX+xZlaRwoeYUxEqBO+Zj4msE5G+BhGBll9lYEKv9Hfks52PAF2X7qDYXQA==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz", + "integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==", "dev": true, "dependencies": { - "esbuild": "^0.18.10", - "postcss": "^8.4.27", - "rollup": "^3.27.1" + "esbuild": "^0.19.3", + "postcss": "^8.4.32", + "rollup": "^4.2.0" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^18.0.0 || >=20.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { - "fsevents": "~2.3.2" + "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": ">= 14", + "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", @@ -18293,395 +17807,6 @@ } } }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", - "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", - "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" - } - }, "node_modules/w3c-xmlserializer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", @@ -18713,17 +17838,6 @@ "node": ">=12.0.0" } }, - "node_modules/wait-on/node_modules/axios": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", - "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", - "dev": true, - "dependencies": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -18907,15 +18021,6 @@ } } }, - "node_modules/webpack-dev-server/node_modules/connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, "node_modules/webpack-dev-server/node_modules/webpack-dev-middleware": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", @@ -19306,15 +18411,6 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, - "node_modules/xmlhttprequest-ssl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", - "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/src-ui/package.json b/src-ui/package.json index 620bef76f..df1149d0d 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -11,17 +11,17 @@ }, "private": true, "dependencies": { - "@angular/cdk": "^17.0.4", - "@angular/common": "~17.0.8", - "@angular/compiler": "~17.0.8", - "@angular/core": "~17.0.8", - "@angular/forms": "~17.0.8", - "@angular/localize": "~17.0.8", - "@angular/platform-browser": "~17.0.8", - "@angular/platform-browser-dynamic": "~17.0.8", - "@angular/router": "~17.0.8", + "@angular/cdk": "^17.1.2", + "@angular/common": "~17.1.2", + "@angular/compiler": "~17.1.2", + "@angular/core": "~17.1.2", + "@angular/forms": "~17.1.2", + "@angular/localize": "~17.1.2", + "@angular/platform-browser": "~17.1.2", + "@angular/platform-browser-dynamic": "~17.1.2", + "@angular/router": "~17.1.2", "@ng-bootstrap/ng-bootstrap": "^16.0.0", - "@ng-select/ng-select": "^12.0.4", + "@ng-select/ng-select": "^12.0.6", "@ngneat/dirty-check-forms": "^3.0.3", "@popperjs/core": "^2.11.8", "bootstrap": "^5.3.2", @@ -31,7 +31,7 @@ "ngx-color": "^9.0.0", "ngx-cookie-service": "^17.0.1", "ngx-file-drop": "^16.0.0", - "ngx-ui-tour-ng-bootstrap": "^14.0.1", + "ngx-ui-tour-ng-bootstrap": "^14.0.2", "pdfjs-dist": "^3.11.174", "rxjs": "^7.8.1", "tslib": "^2.6.2", @@ -40,14 +40,14 @@ }, "devDependencies": { "@angular-builders/jest": "17.0.0", - "@angular-devkit/build-angular": "~17.0.8", - "@angular-eslint/builder": "17.1.1", - "@angular-eslint/eslint-plugin": "17.1.1", - "@angular-eslint/eslint-plugin-template": "17.1.1", - "@angular-eslint/schematics": "17.1.1", - "@angular-eslint/template-parser": "17.1.1", - "@angular/cli": "~17.0.8", - "@angular/compiler-cli": "~17.0.7", + "@angular-devkit/build-angular": "~17.1.2", + "@angular-eslint/builder": "17.2.1", + "@angular-eslint/eslint-plugin": "17.2.1", + "@angular-eslint/eslint-plugin-template": "17.2.1", + "@angular-eslint/schematics": "17.2.1", + "@angular-eslint/template-parser": "17.2.1", + "@angular/cli": "~17.1.2", + "@angular/compiler-cli": "~17.1.2", "@playwright/test": "^1.40.1", "@types/jest": "^29.5.12", "@types/node": "^20.11.16", From c62d892969677d100143607ba76d65f7297672b0 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 1 Feb 2024 18:41:10 -0800 Subject: [PATCH 17/41] Feature: option for auto-remove inbox tags on save (#5562) --- src-ui/messages.xlf | 46 ++++++------- .../admin/settings/settings.component.html | 8 +++ .../admin/settings/settings.component.spec.ts | 2 +- .../admin/settings/settings.component.ts | 8 +++ .../document-detail.component.ts | 4 +- src-ui/src/app/data/document.ts | 3 + src-ui/src/app/data/ui-settings.ts | 7 ++ .../services/rest/document.service.spec.ts | 35 +++++++--- .../src/app/services/rest/document.service.ts | 10 ++- src/documents/serialisers.py | 42 ++++++++++++ src/documents/tests/test_api_documents.py | 66 +++++++++++++++++++ 11 files changed, 196 insertions(+), 35 deletions(-) diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 56866f512..37b0fe848 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -1967,7 +1967,7 @@ src/app/components/document-detail/document-detail.component.ts - 733 + 735 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -2006,7 +2006,7 @@ src/app/components/document-detail/document-detail.component.ts - 735 + 737 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -4856,78 +4856,78 @@ An error occurred loading content: src/app/components/document-detail/document-detail.component.ts - 298,300 + 300,302 Document changes detected src/app/components/document-detail/document-detail.component.ts - 321 + 323 The version of this document in your browser session appears older than the existing version. src/app/components/document-detail/document-detail.component.ts - 322 + 324 Saving the document here may overwrite other changes that were made. To restore the existing version, discard your changes or close the document. src/app/components/document-detail/document-detail.component.ts - 323 + 325 Ok src/app/components/document-detail/document-detail.component.ts - 325 + 327 Error retrieving metadata src/app/components/document-detail/document-detail.component.ts - 465 + 467 Error retrieving suggestions. src/app/components/document-detail/document-detail.component.ts - 490 + 492 Document saved successfully. src/app/components/document-detail/document-detail.component.ts - 608 + 610 src/app/components/document-detail/document-detail.component.ts - 617 + 619 Error saving document src/app/components/document-detail/document-detail.component.ts - 621 + 623 src/app/components/document-detail/document-detail.component.ts - 662 + 664 Confirm delete src/app/components/document-detail/document-detail.component.ts - 688 + 690 src/app/components/manage/management-list/management-list.component.ts @@ -4938,35 +4938,35 @@ Do you really want to delete document ""? src/app/components/document-detail/document-detail.component.ts - 689 + 691 The files for this document will be deleted permanently. This operation cannot be undone. src/app/components/document-detail/document-detail.component.ts - 690 + 692 Delete document src/app/components/document-detail/document-detail.component.ts - 692 + 694 Error deleting document src/app/components/document-detail/document-detail.component.ts - 711 + 713 Redo OCR confirm src/app/components/document-detail/document-detail.component.ts - 731 + 733 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -4977,28 +4977,28 @@ This operation will permanently redo OCR for this document. src/app/components/document-detail/document-detail.component.ts - 732 + 734 Redo OCR operation will begin in the background. Close and re-open or reload this document after the operation has completed to see new content. src/app/components/document-detail/document-detail.component.ts - 743 + 745 Error executing operation src/app/components/document-detail/document-detail.component.ts - 754 + 756 Page Fit src/app/components/document-detail/document-detail.component.ts - 823 + 825 diff --git a/src-ui/src/app/components/admin/settings/settings.component.html b/src-ui/src/app/components/admin/settings/settings.component.html index 52e706f2f..8b239e772 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.html +++ b/src-ui/src/app/components/admin/settings/settings.component.html @@ -158,6 +158,14 @@
+

Document editing

+ +
+
+ +
+
+

Bulk editing

diff --git a/src-ui/src/app/components/admin/settings/settings.component.spec.ts b/src-ui/src/app/components/admin/settings/settings.component.spec.ts index 7ce13c675..6e105ed11 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.spec.ts +++ b/src-ui/src/app/components/admin/settings/settings.component.spec.ts @@ -289,7 +289,7 @@ describe('SettingsComponent', () => { expect(toastErrorSpy).toHaveBeenCalled() expect(storeSpy).toHaveBeenCalled() expect(appearanceSettingsSpy).not.toHaveBeenCalled() - expect(setSpy).toHaveBeenCalledTimes(24) + expect(setSpy).toHaveBeenCalledTimes(25) // succeed storeSpy.mockReturnValueOnce(of(true)) diff --git a/src-ui/src/app/components/admin/settings/settings.component.ts b/src-ui/src/app/components/admin/settings/settings.component.ts index 2bfe5d1c8..a77a556bf 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.ts +++ b/src-ui/src/app/components/admin/settings/settings.component.ts @@ -88,6 +88,7 @@ export class SettingsComponent defaultPermsViewGroups: new FormControl(null), defaultPermsEditUsers: new FormControl(null), defaultPermsEditGroups: new FormControl(null), + documentEditingRemoveInboxTags: new FormControl(null), notificationsConsumerNewDocument: new FormControl(null), notificationsConsumerSuccess: new FormControl(null), @@ -271,6 +272,9 @@ export class SettingsComponent defaultPermsEditGroups: this.settings.get( SETTINGS_KEYS.DEFAULT_PERMS_EDIT_GROUPS ), + documentEditingRemoveInboxTags: this.settings.get( + SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS + ), savedViews: {}, } } @@ -484,6 +488,10 @@ export class SettingsComponent SETTINGS_KEYS.DEFAULT_PERMS_EDIT_GROUPS, this.settingsForm.value.defaultPermsEditGroups ) + this.settings.set( + SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS, + this.settingsForm.value.documentEditingRemoveInboxTags + ) this.settings.setLanguage(this.settingsForm.value.displayLanguage) this.settings .storeSettings() diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts index 0ca458a21..a1162ab7f 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.ts @@ -630,7 +630,9 @@ export class DocumentDetailComponent .update(this.document) .pipe(first()) .subscribe({ - next: () => { + next: (docValues) => { + // in case data changed while saving eg removing inbox_tags + this.documentForm.patchValue(docValues) this.store.next(this.documentForm.value) this.toastService.showInfo($localize`Document saved successfully.`) close && this.close() diff --git a/src-ui/src/app/data/document.ts b/src-ui/src/app/data/document.ts index 2bdb954ce..910666f10 100644 --- a/src-ui/src/app/data/document.ts +++ b/src-ui/src/app/data/document.ts @@ -63,4 +63,7 @@ export interface Document extends ObjectWithPermissions { __search_hit__?: SearchHit custom_fields?: CustomFieldInstance[] + + // write-only field + remove_inbox_tags?: boolean } diff --git a/src-ui/src/app/data/ui-settings.ts b/src-ui/src/app/data/ui-settings.ts index e23e490e9..e55f25278 100644 --- a/src-ui/src/app/data/ui-settings.ts +++ b/src-ui/src/app/data/ui-settings.ts @@ -53,6 +53,8 @@ export const SETTINGS_KEYS = { DEFAULT_PERMS_VIEW_GROUPS: 'general-settings:permissions:default-view-groups', DEFAULT_PERMS_EDIT_USERS: 'general-settings:permissions:default-edit-users', DEFAULT_PERMS_EDIT_GROUPS: 'general-settings:permissions:default-edit-groups', + DOCUMENT_EDITING_REMOVE_INBOX_TAGS: + 'general-settings:document-editing:remove-inbox-tags', } export const SETTINGS: UiSetting[] = [ @@ -206,4 +208,9 @@ export const SETTINGS: UiSetting[] = [ type: 'string', default: '', }, + { + key: SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS, + type: 'boolean', + default: false, + }, ] diff --git a/src-ui/src/app/services/rest/document.service.spec.ts b/src-ui/src/app/services/rest/document.service.spec.ts index 8576a2399..1f3ccc0af 100644 --- a/src-ui/src/app/services/rest/document.service.spec.ts +++ b/src-ui/src/app/services/rest/document.service.spec.ts @@ -7,10 +7,13 @@ import { TestBed } from '@angular/core/testing' import { environment } from 'src/environments/environment' import { DocumentService } from './document.service' import { FILTER_TITLE } from 'src/app/data/filter-rule-type' +import { SettingsService } from '../settings.service' +import { SETTINGS_KEYS } from 'src/app/data/ui-settings' let httpTestingController: HttpTestingController let service: DocumentService let subscription: Subscription +let settingsService: SettingsService const endpoint = 'documents' const documents = [ { @@ -34,6 +37,17 @@ const documents = [ }, ] +beforeEach(() => { + TestBed.configureTestingModule({ + providers: [DocumentService], + imports: [HttpClientTestingModule], + }) + + httpTestingController = TestBed.inject(HttpTestingController) + service = TestBed.inject(DocumentService) + settingsService = TestBed.inject(SettingsService) +}) + describe(`DocumentService`, () => { // common tests e.g. commonAbstractPaperlessServiceTests differ slightly it('should call appropriate api endpoint for list all', () => { @@ -237,16 +251,21 @@ describe(`DocumentService`, () => { ) expect(req.request.method).toEqual('GET') }) -}) -beforeEach(() => { - TestBed.configureTestingModule({ - providers: [DocumentService], - imports: [HttpClientTestingModule], + it('should pass remove_inbox_tags setting to update', () => { + subscription = service.update(documents[0]).subscribe() + let req = httpTestingController.expectOne( + `${environment.apiBaseUrl}${endpoint}/${documents[0].id}/` + ) + expect(req.request.body.remove_inbox_tags).toEqual(false) + + settingsService.set(SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS, true) + subscription = service.update(documents[0]).subscribe() + req = httpTestingController.expectOne( + `${environment.apiBaseUrl}${endpoint}/${documents[0].id}/` + ) + expect(req.request.body.remove_inbox_tags).toEqual(true) }) - - httpTestingController = TestBed.inject(HttpTestingController) - service = TestBed.inject(DocumentService) }) afterEach(() => { diff --git a/src-ui/src/app/services/rest/document.service.ts b/src-ui/src/app/services/rest/document.service.ts index 9ff99031f..37147b818 100644 --- a/src-ui/src/app/services/rest/document.service.ts +++ b/src-ui/src/app/services/rest/document.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core' import { Document } from 'src/app/data/document' import { DocumentMetadata } from 'src/app/data/document-metadata' import { AbstractPaperlessService } from './abstract-paperless-service' -import { HttpClient, HttpParams } from '@angular/common/http' +import { HttpClient } from '@angular/common/http' import { Observable } from 'rxjs' import { Results } from 'src/app/data/results' import { FilterRule } from 'src/app/data/filter-rule' @@ -18,6 +18,8 @@ import { PermissionType, PermissionsService, } from '../permissions.service' +import { SettingsService } from '../settings.service' +import { SETTINGS, SETTINGS_KEYS } from 'src/app/data/ui-settings' export const DOCUMENT_SORT_FIELDS = [ { field: 'archive_serial_number', name: $localize`ASN` }, @@ -63,7 +65,8 @@ export class DocumentService extends AbstractPaperlessService { private documentTypeService: DocumentTypeService, private tagService: TagService, private storagePathService: StoragePathService, - private permissionsService: PermissionsService + private permissionsService: PermissionsService, + private settingsService: SettingsService ) { super(http, 'documents') } @@ -180,6 +183,9 @@ export class DocumentService extends AbstractPaperlessService { update(o: Document): Observable { // we want to only set created_date o.created = undefined + o.remove_inbox_tags = this.settingsService.get( + SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS + ) return super.update(o) } diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 3c65e11d9..0839a14b5 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -638,6 +638,11 @@ class DocumentSerializer( allow_null=True, ) + remove_inbox_tags = serializers.BooleanField( + default=False, + write_only=True, + ) + def get_original_file_name(self, obj): return obj.original_filename @@ -681,12 +686,48 @@ class DocumentSerializer( custom_field_instance.field, doc_id, ) + if ( + "remove_inbox_tags" in validated_data + and validated_data["remove_inbox_tags"] + ): + tag_ids_being_added = ( + [ + tag.id + for tag in validated_data["tags"] + if tag not in instance.tags.all() + ] + if "tags" in validated_data + else [] + ) + inbox_tags_not_being_added = Tag.objects.filter(is_inbox_tag=True).exclude( + id__in=tag_ids_being_added, + ) + if "tags" in validated_data: + validated_data["tags"] = [ + tag + for tag in validated_data["tags"] + if tag not in inbox_tags_not_being_added + ] + else: + validated_data["tags"] = [ + tag + for tag in instance.tags.all() + if tag not in inbox_tags_not_being_added + ] super().update(instance, validated_data) return instance def __init__(self, *args, **kwargs): self.truncate_content = kwargs.pop("truncate_content", False) + # return full permissions if we're doing a PATCH or PUT + context = kwargs.get("context") + if ( + context.get("request").method == "PATCH" + or context.get("request").method == "PUT" + ): + kwargs["full_perms"] = True + super().__init__(*args, **kwargs) class Meta: @@ -714,6 +755,7 @@ class DocumentSerializer( "set_permissions", "notes", "custom_fields", + "remove_inbox_tags", ) diff --git a/src/documents/tests/test_api_documents.py b/src/documents/tests/test_api_documents.py index 510e2b1b3..20dd64d82 100644 --- a/src/documents/tests/test_api_documents.py +++ b/src/documents/tests/test_api_documents.py @@ -2080,6 +2080,72 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): self.assertEqual(resp.status_code, status.HTTP_200_OK) self.assertEqual(resp.content, b"1") + def test_remove_inbox_tags(self): + """ + GIVEN: + - Existing document with or without inbox tags + WHEN: + - API request to update document, with or without `remove_inbox_tags` flag + THEN: + - Inbox tags are removed as long as they are not being added + """ + tag1 = Tag.objects.create(name="tag1", color="#abcdef") + inbox_tag1 = Tag.objects.create( + name="inbox1", + color="#abcdef", + is_inbox_tag=True, + ) + inbox_tag2 = Tag.objects.create( + name="inbox2", + color="#abcdef", + is_inbox_tag=True, + ) + + doc1 = Document.objects.create( + title="test", + mime_type="application/pdf", + content="this is a document 1", + checksum="1", + ) + doc1.tags.add(tag1) + doc1.tags.add(inbox_tag1) + doc1.tags.add(inbox_tag2) + doc1.save() + + # Remove inbox tags defaults to false + resp = self.client.patch( + f"/api/documents/{doc1.pk}/", + { + "title": "New title", + }, + ) + doc1.refresh_from_db() + self.assertEqual(resp.status_code, status.HTTP_200_OK) + self.assertEqual(doc1.tags.count(), 3) + + # Remove inbox tags set to true + resp = self.client.patch( + f"/api/documents/{doc1.pk}/", + { + "remove_inbox_tags": True, + }, + ) + doc1.refresh_from_db() + self.assertEqual(resp.status_code, status.HTTP_200_OK) + self.assertEqual(doc1.tags.count(), 1) + + # Remove inbox tags set to true but adding a new inbox tag + resp = self.client.patch( + f"/api/documents/{doc1.pk}/", + { + "remove_inbox_tags": True, + "tags": [inbox_tag1.pk, tag1.pk], + }, + ) + doc1.refresh_from_db() + self.assertEqual(resp.status_code, status.HTTP_200_OK) + self.assertEqual(doc1.tags.count(), 2) + class TestDocumentApiV2(DirectoriesMixin, APITestCase): def setUp(self): From b55529b9133cfbd9e8e9d6d69d211101327e5c3a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 22:06:10 -0800 Subject: [PATCH 18/41] Chore(deps-dev): Bump @playwright/test from 1.40.1 to 1.41.2 in /src-ui (#5634) * Chore(deps-dev): Bump @playwright/test from 1.40.1 to 1.41.2 in /src-ui Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.40.1 to 1.41.2. - [Release notes](https://github.com/microsoft/playwright/releases) - [Commits](https://github.com/microsoft/playwright/compare/v1.40.1...v1.41.2) --- updated-dependencies: - dependency-name: "@playwright/test" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Update tests for breaking playwright change --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com> --- .../e2e/dashboard/requests/api-dashboard2.har | 38 +++++------ .../e2e/dashboard/requests/api-dashboard4.har | 10 +-- .../requests/api-document-detail.har | 4 +- .../requests/api-document-detail2.har | 64 +++++++++---------- src-ui/package-lock.json | 24 +++---- src-ui/package.json | 2 +- 6 files changed, 71 insertions(+), 71 deletions(-) diff --git a/src-ui/e2e/dashboard/requests/api-dashboard2.har b/src-ui/e2e/dashboard/requests/api-dashboard2.har index 2436a6272..b3c2dda5a 100644 --- a/src-ui/e2e/dashboard/requests/api-dashboard2.har +++ b/src-ui/e2e/dashboard/requests/api-dashboard2.har @@ -2700,7 +2700,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2734,7 +2734,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2768,7 +2768,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2802,7 +2802,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2836,7 +2836,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2870,7 +2870,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2904,7 +2904,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2938,7 +2938,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2972,7 +2972,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -3006,7 +3006,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -3040,7 +3040,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -3074,7 +3074,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -3108,7 +3108,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -3142,7 +3142,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -3176,7 +3176,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -3210,7 +3210,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -3244,7 +3244,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -3278,7 +3278,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -3312,7 +3312,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], diff --git a/src-ui/e2e/dashboard/requests/api-dashboard4.har b/src-ui/e2e/dashboard/requests/api-dashboard4.har index ca0101d59..dff0c3202 100644 --- a/src-ui/e2e/dashboard/requests/api-dashboard4.har +++ b/src-ui/e2e/dashboard/requests/api-dashboard4.har @@ -425,7 +425,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -470,7 +470,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -645,7 +645,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -685,7 +685,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -729,7 +729,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], diff --git a/src-ui/e2e/document-detail/requests/api-document-detail.har b/src-ui/e2e/document-detail/requests/api-document-detail.har index c3892a121..15705a4b3 100644 --- a/src-ui/e2e/document-detail/requests/api-document-detail.har +++ b/src-ui/e2e/document-detail/requests/api-document-detail.har @@ -843,7 +843,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -994,7 +994,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], diff --git a/src-ui/e2e/document-detail/requests/api-document-detail2.har b/src-ui/e2e/document-detail/requests/api-document-detail2.har index ee3166211..30cf005b8 100644 --- a/src-ui/e2e/document-detail/requests/api-document-detail2.har +++ b/src-ui/e2e/document-detail/requests/api-document-detail2.har @@ -996,7 +996,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -1301,7 +1301,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -1484,7 +1484,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -1518,7 +1518,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -1552,7 +1552,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -1586,7 +1586,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -1620,7 +1620,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -1654,7 +1654,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -1688,7 +1688,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -1722,7 +1722,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -1756,7 +1756,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -1790,7 +1790,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -1824,7 +1824,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -1858,7 +1858,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -1892,7 +1892,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -1926,7 +1926,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -1960,7 +1960,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -1994,7 +1994,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2028,7 +2028,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2062,7 +2062,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2096,7 +2096,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2130,7 +2130,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2164,7 +2164,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2198,7 +2198,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2232,7 +2232,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2266,7 +2266,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2300,7 +2300,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2334,7 +2334,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2368,7 +2368,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2402,7 +2402,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2436,7 +2436,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], @@ -2470,7 +2470,7 @@ "bodySize": -1 }, "response": { - "status": -1, + "status": 200, "statusText": "", "httpVersion": "HTTP/1.1", "cookies": [], diff --git a/src-ui/package-lock.json b/src-ui/package-lock.json index 1076db189..d554b98cd 100644 --- a/src-ui/package-lock.json +++ b/src-ui/package-lock.json @@ -46,7 +46,7 @@ "@angular-eslint/template-parser": "17.2.1", "@angular/cli": "~17.1.2", "@angular/compiler-cli": "~17.1.2", - "@playwright/test": "^1.40.1", + "@playwright/test": "^1.41.2", "@types/jest": "^29.5.12", "@types/node": "^20.11.16", "@typescript-eslint/eslint-plugin": "^6.20.0", @@ -4913,12 +4913,12 @@ } }, "node_modules/@playwright/test": { - "version": "1.40.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.40.1.tgz", - "integrity": "sha512-EaaawMTOeEItCRvfmkI9v6rBkF1svM8wjl/YPRrg2N2Wmp+4qJYkWtJsbew1szfKKDm6fPLy4YAanBhIlf9dWw==", + "version": "1.41.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.2.tgz", + "integrity": "sha512-qQB9h7KbibJzrDpkXkYvsmiDJK14FULCCZgEcoe2AvFAS64oCirWTwzTlAYEbKaRxWs5TFesE1Na6izMv3HfGg==", "dev": true, "dependencies": { - "playwright": "1.40.1" + "playwright": "1.41.2" }, "bin": { "playwright": "cli.js" @@ -15250,12 +15250,12 @@ } }, "node_modules/playwright": { - "version": "1.40.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.40.1.tgz", - "integrity": "sha512-2eHI7IioIpQ0bS1Ovg/HszsN/XKNwEG1kbzSDDmADpclKc7CyqkHw7Mg2JCz/bbCxg25QUPcjksoMW7JcIFQmw==", + "version": "1.41.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.2.tgz", + "integrity": "sha512-v0bOa6H2GJChDL8pAeLa/LZC4feoAMbSQm1/jF/ySsWWoaNItvrMP7GEkvEEFyCTUYKMxjQKaTSg5up7nR6/8A==", "dev": true, "dependencies": { - "playwright-core": "1.40.1" + "playwright-core": "1.41.2" }, "bin": { "playwright": "cli.js" @@ -15268,9 +15268,9 @@ } }, "node_modules/playwright-core": { - "version": "1.40.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.1.tgz", - "integrity": "sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ==", + "version": "1.41.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.2.tgz", + "integrity": "sha512-VaTvwCA4Y8kxEe+kfm2+uUUw5Lubf38RxF7FpBxLPmGe5sdNkSg5e3ChEigaGrX7qdqT3pt2m/98LiyvU2x6CA==", "dev": true, "bin": { "playwright-core": "cli.js" diff --git a/src-ui/package.json b/src-ui/package.json index df1149d0d..72e03e270 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -48,7 +48,7 @@ "@angular-eslint/template-parser": "17.2.1", "@angular/cli": "~17.1.2", "@angular/compiler-cli": "~17.1.2", - "@playwright/test": "^1.40.1", + "@playwright/test": "^1.41.2", "@types/jest": "^29.5.12", "@types/node": "^20.11.16", "@typescript-eslint/eslint-plugin": "^6.20.0", From dfd959839ffabff2284d11032a3e7cfcddb7cd20 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Fri, 2 Feb 2024 20:16:14 -0800 Subject: [PATCH 19/41] Update translation strings --- src-ui/messages.xlf | 360 ++++++++++---------- src/locale/en_US/LC_MESSAGES/django.po | 450 +++++++++++++------------ 2 files changed, 416 insertions(+), 394 deletions(-) diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 37b0fe848..3bf02bf6c 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -458,7 +458,7 @@ src/app/components/admin/settings/settings.component.html - 338 + 346 src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.html @@ -600,7 +600,7 @@ src/app/components/admin/settings/settings.component.html - 326 + 334 src/app/components/admin/tasks/tasks.component.html @@ -667,7 +667,7 @@ src/app/components/admin/settings/settings.component.html - 285 + 293 src/app/components/app-frame/app-frame.component.html @@ -878,39 +878,53 @@ 157 + + Document editing + + src/app/components/admin/settings/settings.component.html + 161 + + + + Automatically remove inbox tag(s) on save + + src/app/components/admin/settings/settings.component.html + 165 + + Bulk editing src/app/components/admin/settings/settings.component.html - 161 + 169 Show confirmation dialogs src/app/components/admin/settings/settings.component.html - 165 + 173 Deleting documents will always ask for confirmation. src/app/components/admin/settings/settings.component.html - 165 + 173 Apply on close src/app/components/admin/settings/settings.component.html - 166 + 174 Notes src/app/components/admin/settings/settings.component.html - 170 + 178 src/app/components/document-list/document-list.component.html @@ -918,21 +932,21 @@ src/app/services/rest/document.service.ts - 25 + 32 Enable notes src/app/components/admin/settings/settings.component.html - 174 + 182 Permissions src/app/components/admin/settings/settings.component.html - 182 + 190 src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.html @@ -952,11 +966,11 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.html - 70 + 78 src/app/components/document-list/filter-editor/filter-editor.component.html - 82 + 90 src/app/components/manage/mail/mail.component.html @@ -987,28 +1001,28 @@ Default Permissions src/app/components/admin/settings/settings.component.html - 185 + 193 Settings apply to this user account for objects (Tags, Mail Rules, etc.) created via the web UI src/app/components/admin/settings/settings.component.html - 189,191 + 197,199 Default Owner src/app/components/admin/settings/settings.component.html - 196 + 204 Objects without an owner can be viewed and edited by all users src/app/components/admin/settings/settings.component.html - 200 + 208 src/app/components/common/input/permissions/permissions-form/permissions-form.component.html @@ -1019,18 +1033,18 @@ Default View Permissions src/app/components/admin/settings/settings.component.html - 205 + 213 Users: src/app/components/admin/settings/settings.component.html - 210 + 218 src/app/components/admin/settings/settings.component.html - 237 + 245 src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html @@ -1053,11 +1067,11 @@ Groups: src/app/components/admin/settings/settings.component.html - 220 + 228 src/app/components/admin/settings/settings.component.html - 247 + 255 src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html @@ -1080,14 +1094,14 @@ Default Edit Permissions src/app/components/admin/settings/settings.component.html - 232 + 240 Edit permissions also grant viewing permissions src/app/components/admin/settings/settings.component.html - 256 + 264 src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html @@ -1102,56 +1116,56 @@ Notifications src/app/components/admin/settings/settings.component.html - 264 + 272 Document processing src/app/components/admin/settings/settings.component.html - 267 + 275 Show notifications when new documents are detected src/app/components/admin/settings/settings.component.html - 271 + 279 Show notifications when document processing completes successfully src/app/components/admin/settings/settings.component.html - 272 + 280 Show notifications when document processing fails src/app/components/admin/settings/settings.component.html - 273 + 281 Suppress notifications on dashboard src/app/components/admin/settings/settings.component.html - 274 + 282 This will suppress all messages about document processing status on the dashboard. src/app/components/admin/settings/settings.component.html - 274 + 282 Saved views src/app/components/admin/settings/settings.component.html - 282 + 290 src/app/components/app-frame/app-frame.component.html @@ -1162,14 +1176,14 @@ Show warning when closing saved views with unsaved changes src/app/components/admin/settings/settings.component.html - 288 + 296 Views src/app/components/admin/settings/settings.component.html - 292 + 300 src/app/components/document-list/document-list.component.html @@ -1180,7 +1194,7 @@ Name src/app/components/admin/settings/settings.component.html - 298 + 306 src/app/components/admin/tasks/tasks.component.html @@ -1287,14 +1301,14 @@  Appears on src/app/components/admin/settings/settings.component.html - 302,303 + 310,311 Show on dashboard src/app/components/admin/settings/settings.component.html - 305 + 313 src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html @@ -1305,7 +1319,7 @@ Show in sidebar src/app/components/admin/settings/settings.component.html - 309 + 317 src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html @@ -1316,7 +1330,7 @@ Actions src/app/components/admin/settings/settings.component.html - 313 + 321 src/app/components/admin/tasks/tasks.component.html @@ -1340,7 +1354,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.html - 76 + 84 src/app/components/manage/custom-fields/custom-fields.component.html @@ -1379,7 +1393,7 @@ Delete src/app/components/admin/settings/settings.component.html - 314 + 322 src/app/components/admin/users-groups/users-groups.component.html @@ -1415,7 +1429,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.html - 128 + 136 src/app/components/manage/custom-fields/custom-fields.component.html @@ -1474,7 +1488,7 @@ No saved views defined. src/app/components/admin/settings/settings.component.html - 320 + 328 @@ -1495,7 +1509,7 @@ Error retrieving users src/app/components/admin/settings/settings.component.ts - 158 + 159 src/app/components/admin/users-groups/users-groups.component.ts @@ -1506,7 +1520,7 @@ Error retrieving groups src/app/components/admin/settings/settings.component.ts - 177 + 178 src/app/components/admin/users-groups/users-groups.component.ts @@ -1517,35 +1531,35 @@ Saved view "" deleted. src/app/components/admin/settings/settings.component.ts - 376 + 380 Settings were saved successfully. src/app/components/admin/settings/settings.component.ts - 498 + 506 Settings were saved successfully. Reload is required to apply some changes. src/app/components/admin/settings/settings.component.ts - 502 + 510 Reload now src/app/components/admin/settings/settings.component.ts - 503 + 511 An error occurred while saving settings. src/app/components/admin/settings/settings.component.ts - 513 + 521 src/app/components/app-frame/app-frame.component.ts @@ -1556,7 +1570,7 @@ Error while storing settings on server. src/app/components/admin/settings/settings.component.ts - 547 + 555 @@ -1613,11 +1627,11 @@ src/app/components/document-list/filter-editor/filter-editor.component.html - 68 + 76 src/app/services/rest/document.service.ts - 22 + 29 @@ -1787,7 +1801,7 @@ src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.html - 76 + 77 @@ -1843,7 +1857,7 @@ src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html - 27 + 33 src/app/components/document-list/document-card-large/document-card-large.component.html @@ -1967,15 +1981,15 @@ src/app/components/document-detail/document-detail.component.ts - 735 + 762 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 461 + 489 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 500 + 528 src/app/components/manage/custom-fields/custom-fields.component.ts @@ -2006,11 +2020,11 @@ src/app/components/document-detail/document-detail.component.ts - 737 + 764 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 502 + 530 src/app/components/manage/custom-fields/custom-fields.component.ts @@ -2237,7 +2251,7 @@ src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html - 18 + 19 src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html @@ -2245,11 +2259,11 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.html - 20 + 21 src/app/components/document-list/filter-editor/filter-editor.component.html - 32 + 33 @@ -2442,19 +2456,19 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 323 + 351 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 363 + 391 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 401 + 429 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 439 + 467 @@ -3990,7 +4004,7 @@ Hide unowned src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.html - 86 + 88 @@ -4341,18 +4355,18 @@ src/app/components/document-list/filter-editor/filter-editor.component.ts - 109 + 115 src/app/services/rest/document.service.ts - 20 + 27 Correspondent src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html - 19 + 22 src/app/components/document-detail/document-detail.component.html @@ -4360,7 +4374,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.html - 32 + 35 src/app/components/document-list/document-list.component.html @@ -4368,25 +4382,25 @@ src/app/components/document-list/filter-editor/filter-editor.component.html - 41 + 44 src/app/services/rest/document.service.ts - 19 + 26 View Preview src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html - 39 + 47 Download src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html - 47 + 55 src/app/components/document-detail/document-detail.component.html @@ -4394,7 +4408,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.html - 94 + 102 src/app/components/document-list/document-card-large/document-card-large.component.html @@ -4409,7 +4423,7 @@ No documents src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html - 57 + 65 @@ -4527,7 +4541,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 268 + 296 this string is used to separate processing, failed and added on the file upload widget @@ -4620,7 +4634,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.html - 79 + 87 @@ -4681,7 +4695,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.html - 43 + 48 src/app/components/document-list/document-list.component.html @@ -4689,11 +4703,11 @@ src/app/components/document-list/filter-editor/filter-editor.component.html - 49 + 54 src/app/services/rest/document.service.ts - 21 + 28 @@ -4704,7 +4718,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.html - 54 + 61 src/app/components/document-list/document-list.component.html @@ -4712,7 +4726,7 @@ src/app/components/document-list/filter-editor/filter-editor.component.html - 57 + 64 @@ -4856,78 +4870,78 @@ An error occurred loading content: src/app/components/document-detail/document-detail.component.ts - 300,302 + 325,327 Document changes detected src/app/components/document-detail/document-detail.component.ts - 323 + 348 The version of this document in your browser session appears older than the existing version. src/app/components/document-detail/document-detail.component.ts - 324 + 349 Saving the document here may overwrite other changes that were made. To restore the existing version, discard your changes or close the document. src/app/components/document-detail/document-detail.component.ts - 325 + 350 Ok src/app/components/document-detail/document-detail.component.ts - 327 + 352 Error retrieving metadata src/app/components/document-detail/document-detail.component.ts - 467 + 492 Error retrieving suggestions. src/app/components/document-detail/document-detail.component.ts - 492 + 517 Document saved successfully. src/app/components/document-detail/document-detail.component.ts - 610 + 637 src/app/components/document-detail/document-detail.component.ts - 619 + 646 Error saving document src/app/components/document-detail/document-detail.component.ts - 623 + 650 src/app/components/document-detail/document-detail.component.ts - 664 + 691 Confirm delete src/app/components/document-detail/document-detail.component.ts - 690 + 717 src/app/components/manage/management-list/management-list.component.ts @@ -4938,67 +4952,67 @@ Do you really want to delete document ""? src/app/components/document-detail/document-detail.component.ts - 691 + 718 The files for this document will be deleted permanently. This operation cannot be undone. src/app/components/document-detail/document-detail.component.ts - 692 + 719 Delete document src/app/components/document-detail/document-detail.component.ts - 694 + 721 Error deleting document src/app/components/document-detail/document-detail.component.ts - 713 + 740 Redo OCR confirm src/app/components/document-detail/document-detail.component.ts - 733 + 760 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 498 + 526 This operation will permanently redo OCR for this document. src/app/components/document-detail/document-detail.component.ts - 734 + 761 Redo OCR operation will begin in the background. Close and re-open or reload this document after the operation has completed to see new content. src/app/components/document-detail/document-detail.component.ts - 745 + 772 Error executing operation src/app/components/document-detail/document-detail.component.ts - 756 + 783 Page Fit src/app/components/document-detail/document-detail.component.ts - 825 + 852 @@ -5019,97 +5033,97 @@ Filter tags src/app/components/document-list/bulk-editor/bulk-editor.component.html - 21 + 22 src/app/components/document-list/filter-editor/filter-editor.component.html - 33 + 34 Filter correspondents src/app/components/document-list/bulk-editor/bulk-editor.component.html - 33 + 36 src/app/components/document-list/filter-editor/filter-editor.component.html - 42 + 45 Filter document types src/app/components/document-list/bulk-editor/bulk-editor.component.html - 44 + 49 src/app/components/document-list/filter-editor/filter-editor.component.html - 50 + 55 Filter storage paths src/app/components/document-list/bulk-editor/bulk-editor.component.html - 55 + 62 src/app/components/document-list/filter-editor/filter-editor.component.html - 58 + 65 Include: src/app/components/document-list/bulk-editor/bulk-editor.component.html - 100 + 108 Archived files src/app/components/document-list/bulk-editor/bulk-editor.component.html - 104,106 + 112,114 Original files src/app/components/document-list/bulk-editor/bulk-editor.component.html - 110,112 + 118,120 Use formatted filename src/app/components/document-list/bulk-editor/bulk-editor.component.html - 117,119 + 125,127 Error executing bulk operation src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 185 + 213 "" src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 260 + 288 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 266 + 294 "" and "" src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 262 + 290 This is for messages like 'modify "tag1" and "tag2"' @@ -5117,7 +5131,7 @@ and "" src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 270,272 + 298,300 this is for messages like 'modify "tag1", "tag2" and "tag3"' @@ -5125,14 +5139,14 @@ Confirm tags assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 287 + 315 This operation will add the tag "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 293 + 321 @@ -5141,14 +5155,14 @@ )"/> to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 298,300 + 326,328 This operation will remove the tag "" from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 306 + 334 @@ -5157,7 +5171,7 @@ )"/> from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 311,313 + 339,341 @@ -5168,98 +5182,98 @@ )"/> on selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 315,319 + 343,347 Confirm correspondent assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 356 + 384 This operation will assign the correspondent "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 358 + 386 This operation will remove the correspondent from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 360 + 388 Confirm document type assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 394 + 422 This operation will assign the document type "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 396 + 424 This operation will remove the document type from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 398 + 426 Confirm storage path assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 432 + 460 This operation will assign the storage path "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 434 + 462 This operation will remove the storage path from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 436 + 464 Delete confirm src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 459 + 487 This operation will permanently delete selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 460 + 488 Delete document(s) src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 463 + 491 This operation will permanently redo OCR for selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 499 + 527 @@ -5474,7 +5488,7 @@ src/app/components/document-list/filter-editor/filter-editor.component.html - 88 + 96 @@ -5499,11 +5513,11 @@ src/app/components/document-list/filter-editor/filter-editor.component.ts - 114 + 120 src/app/services/rest/document.service.ts - 18 + 25 @@ -5535,7 +5549,7 @@ src/app/services/rest/document.service.ts - 26 + 33 @@ -5581,11 +5595,11 @@ src/app/components/document-list/filter-editor/filter-editor.component.html - 74 + 82 src/app/services/rest/document.service.ts - 23 + 30 @@ -5613,63 +5627,63 @@ Title & content src/app/components/document-list/filter-editor/filter-editor.component.ts - 112 + 118 Custom fields src/app/components/document-list/filter-editor/filter-editor.component.ts - 117 + 123 Advanced search src/app/components/document-list/filter-editor/filter-editor.component.ts - 121 + 127 More like src/app/components/document-list/filter-editor/filter-editor.component.ts - 127 + 133 equals src/app/components/document-list/filter-editor/filter-editor.component.ts - 133 + 139 is empty src/app/components/document-list/filter-editor/filter-editor.component.ts - 137 + 143 is not empty src/app/components/document-list/filter-editor/filter-editor.component.ts - 141 + 147 greater than src/app/components/document-list/filter-editor/filter-editor.component.ts - 145 + 151 less than src/app/components/document-list/filter-editor/filter-editor.component.ts - 149 + 155 @@ -5678,14 +5692,14 @@ )?.name"/> src/app/components/document-list/filter-editor/filter-editor.component.ts - 166,168 + 175,177 Without correspondent src/app/components/document-list/filter-editor/filter-editor.component.ts - 170 + 179 @@ -5694,14 +5708,14 @@ )?.name"/> src/app/components/document-list/filter-editor/filter-editor.component.ts - 176,178 + 185,187 Without document type src/app/components/document-list/filter-editor/filter-editor.component.ts - 180 + 189 @@ -5710,14 +5724,14 @@ )?.name"/> src/app/components/document-list/filter-editor/filter-editor.component.ts - 186,188 + 195,197 Without storage path src/app/components/document-list/filter-editor/filter-editor.component.ts - 190 + 199 @@ -5725,49 +5739,49 @@ ?.name"/> src/app/components/document-list/filter-editor/filter-editor.component.ts - 194,195 + 203,204 Without any tag src/app/components/document-list/filter-editor/filter-editor.component.ts - 199 + 208 Title: src/app/components/document-list/filter-editor/filter-editor.component.ts - 203 + 212 ASN: src/app/components/document-list/filter-editor/filter-editor.component.ts - 206 + 215 Owner: src/app/components/document-list/filter-editor/filter-editor.component.ts - 209 + 218 Owner not in: src/app/components/document-list/filter-editor/filter-editor.component.ts - 212 + 221 Without an owner src/app/components/document-list/filter-editor/filter-editor.component.ts - 215 + 224 @@ -6920,14 +6934,14 @@ Modified src/app/services/rest/document.service.ts - 24 + 31 Search score src/app/services/rest/document.service.ts - 33 + 40 Score is a value returned by the full text search engine and specifies how well a result matches the given query diff --git a/src/locale/en_US/LC_MESSAGES/django.po b/src/locale/en_US/LC_MESSAGES/django.po index ce77ebe79..83398f10c 100644 --- a/src/locale/en_US/LC_MESSAGES/django.po +++ b/src/locale/en_US/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: paperless-ngx\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-01-05 21:26-0800\n" +"POT-Creation-Date: 2024-02-02 20:15-0800\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:736 +#: documents/models.py:36 documents/models.py:739 msgid "owner" msgstr "" -#: documents/models.py:53 documents/models.py:894 +#: documents/models.py:53 documents/models.py:897 msgid "None" msgstr "" -#: documents/models.py:54 documents/models.py:895 +#: documents/models.py:54 documents/models.py:898 msgid "Any word" msgstr "" -#: documents/models.py:55 documents/models.py:896 +#: documents/models.py:55 documents/models.py:899 msgid "All words" msgstr "" -#: documents/models.py:56 documents/models.py:897 +#: documents/models.py:56 documents/models.py:900 msgid "Exact match" msgstr "" -#: documents/models.py:57 documents/models.py:898 +#: documents/models.py:57 documents/models.py:901 msgid "Regular expression" msgstr "" -#: documents/models.py:58 documents/models.py:899 +#: documents/models.py:58 documents/models.py:902 msgid "Fuzzy word" msgstr "" @@ -53,20 +53,20 @@ msgstr "" msgid "Automatic" msgstr "" -#: documents/models.py:62 documents/models.py:402 documents/models.py:1099 +#: documents/models.py:62 documents/models.py:397 documents/models.py:1102 #: paperless_mail/models.py:18 paperless_mail/models.py:93 msgid "name" msgstr "" -#: documents/models.py:64 documents/models.py:955 +#: documents/models.py:64 documents/models.py:958 msgid "match" msgstr "" -#: documents/models.py:67 documents/models.py:958 +#: documents/models.py:67 documents/models.py:961 msgid "matching algorithm" msgstr "" -#: documents/models.py:72 documents/models.py:963 +#: documents/models.py:72 documents/models.py:966 msgid "is insensitive" msgstr "" @@ -132,7 +132,7 @@ msgstr "" msgid "title" msgstr "" -#: documents/models.py:171 documents/models.py:650 +#: documents/models.py:171 documents/models.py:653 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:656 -#: documents/models.py:694 documents/models.py:764 documents/models.py:801 +#: documents/models.py:205 documents/models.py:385 documents/models.py:659 +#: documents/models.py:697 documents/models.py:767 documents/models.py:804 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:667 documents/models.py:721 +#: documents/models.py:279 documents/models.py:670 documents/models.py:724 msgid "document" msgstr "" @@ -259,530 +259,530 @@ msgstr "" msgid "logs" msgstr "" -#: documents/models.py:399 documents/models.py:466 -msgid "saved view" -msgstr "" - #: documents/models.py:400 -msgid "saved views" -msgstr "" - -#: documents/models.py:405 msgid "show on dashboard" msgstr "" -#: documents/models.py:408 +#: documents/models.py:403 msgid "show in sidebar" msgstr "" -#: documents/models.py:412 +#: documents/models.py:407 msgid "sort field" msgstr "" -#: documents/models.py:417 +#: documents/models.py:412 msgid "sort reverse" msgstr "" -#: documents/models.py:422 -msgid "title contains" +#: documents/models.py:416 documents/models.py:469 +msgid "saved view" msgstr "" -#: documents/models.py:423 -msgid "content contains" -msgstr "" - -#: documents/models.py:424 -msgid "ASN is" +#: documents/models.py:417 +msgid "saved views" msgstr "" #: documents/models.py:425 -msgid "correspondent is" +msgid "title contains" msgstr "" #: documents/models.py:426 -msgid "document type is" +msgid "content contains" msgstr "" #: documents/models.py:427 -msgid "is in inbox" +msgid "ASN is" msgstr "" #: documents/models.py:428 -msgid "has tag" +msgid "correspondent is" msgstr "" #: documents/models.py:429 -msgid "has any tag" +msgid "document type is" msgstr "" #: documents/models.py:430 -msgid "created before" +msgid "is in inbox" msgstr "" #: documents/models.py:431 -msgid "created after" +msgid "has tag" msgstr "" #: documents/models.py:432 -msgid "created year is" +msgid "has any tag" msgstr "" #: documents/models.py:433 -msgid "created month is" +msgid "created before" msgstr "" #: documents/models.py:434 -msgid "created day is" +msgid "created after" msgstr "" #: documents/models.py:435 -msgid "added before" +msgid "created year is" msgstr "" #: documents/models.py:436 -msgid "added after" +msgid "created month is" msgstr "" #: documents/models.py:437 -msgid "modified before" +msgid "created day is" msgstr "" #: documents/models.py:438 -msgid "modified after" +msgid "added before" msgstr "" #: documents/models.py:439 -msgid "does not have tag" +msgid "added after" msgstr "" #: documents/models.py:440 -msgid "does not have ASN" +msgid "modified before" msgstr "" #: documents/models.py:441 -msgid "title or content contains" +msgid "modified after" msgstr "" #: documents/models.py:442 -msgid "fulltext query" +msgid "does not have tag" msgstr "" #: documents/models.py:443 -msgid "more like this" +msgid "does not have ASN" msgstr "" #: documents/models.py:444 -msgid "has tags in" +msgid "title or content contains" msgstr "" #: documents/models.py:445 -msgid "ASN greater than" +msgid "fulltext query" msgstr "" #: documents/models.py:446 -msgid "ASN less than" +msgid "more like this" msgstr "" #: documents/models.py:447 -msgid "storage path is" +msgid "has tags in" msgstr "" #: documents/models.py:448 -msgid "has correspondent in" +msgid "ASN greater than" msgstr "" #: documents/models.py:449 -msgid "does not have correspondent in" +msgid "ASN less than" msgstr "" #: documents/models.py:450 -msgid "has document type in" +msgid "storage path is" msgstr "" #: documents/models.py:451 -msgid "does not have document type in" +msgid "has correspondent in" msgstr "" #: documents/models.py:452 -msgid "has storage path in" +msgid "does not have correspondent in" msgstr "" #: documents/models.py:453 -msgid "does not have storage path in" +msgid "has document type in" msgstr "" #: documents/models.py:454 -msgid "owner is" +msgid "does not have document type in" msgstr "" #: documents/models.py:455 -msgid "has owner in" +msgid "has storage path in" msgstr "" #: documents/models.py:456 -msgid "does not have owner" +msgid "does not have storage path in" msgstr "" #: documents/models.py:457 -msgid "does not have owner in" +msgid "owner is" msgstr "" #: documents/models.py:458 -msgid "has custom field value" +msgid "has owner in" msgstr "" #: documents/models.py:459 +msgid "does not have owner" +msgstr "" + +#: documents/models.py:460 +msgid "does not have owner in" +msgstr "" + +#: documents/models.py:461 +msgid "has custom field value" +msgstr "" + +#: documents/models.py:462 msgid "is shared by me" msgstr "" -#: documents/models.py:469 +#: documents/models.py:472 msgid "rule type" msgstr "" -#: documents/models.py:471 +#: documents/models.py:474 msgid "value" msgstr "" -#: documents/models.py:474 +#: documents/models.py:477 msgid "filter rule" msgstr "" -#: documents/models.py:475 +#: documents/models.py:478 msgid "filter rules" msgstr "" -#: documents/models.py:586 +#: documents/models.py:589 msgid "Task ID" msgstr "" -#: documents/models.py:587 +#: documents/models.py:590 msgid "Celery ID for the Task that was run" msgstr "" -#: documents/models.py:592 +#: documents/models.py:595 msgid "Acknowledged" msgstr "" -#: documents/models.py:593 +#: documents/models.py:596 msgid "If the task is acknowledged via the frontend or API" msgstr "" -#: documents/models.py:599 +#: documents/models.py:602 msgid "Task Filename" msgstr "" -#: documents/models.py:600 +#: documents/models.py:603 msgid "Name of the file which the Task was run for" msgstr "" -#: documents/models.py:606 +#: documents/models.py:609 msgid "Task Name" msgstr "" -#: documents/models.py:607 +#: documents/models.py:610 msgid "Name of the Task which was run" msgstr "" -#: documents/models.py:614 +#: documents/models.py:617 msgid "Task State" msgstr "" -#: documents/models.py:615 +#: documents/models.py:618 msgid "Current state of the task being run" msgstr "" -#: documents/models.py:620 +#: documents/models.py:623 msgid "Created DateTime" msgstr "" -#: documents/models.py:621 +#: documents/models.py:624 msgid "Datetime field when the task result was created in UTC" msgstr "" -#: documents/models.py:626 +#: documents/models.py:629 msgid "Started DateTime" msgstr "" -#: documents/models.py:627 +#: documents/models.py:630 msgid "Datetime field when the task was started in UTC" msgstr "" -#: documents/models.py:632 +#: documents/models.py:635 msgid "Completed DateTime" msgstr "" -#: documents/models.py:633 +#: documents/models.py:636 msgid "Datetime field when the task was completed in UTC" msgstr "" -#: documents/models.py:638 +#: documents/models.py:641 msgid "Result Data" msgstr "" -#: documents/models.py:640 +#: documents/models.py:643 msgid "The data returned by the task" msgstr "" -#: documents/models.py:652 +#: documents/models.py:655 msgid "Note for the document" msgstr "" -#: documents/models.py:676 +#: documents/models.py:679 msgid "user" msgstr "" -#: documents/models.py:681 +#: documents/models.py:684 msgid "note" msgstr "" -#: documents/models.py:682 +#: documents/models.py:685 msgid "notes" msgstr "" -#: documents/models.py:690 +#: documents/models.py:693 msgid "Archive" msgstr "" -#: documents/models.py:691 +#: documents/models.py:694 msgid "Original" msgstr "" -#: documents/models.py:702 +#: documents/models.py:705 msgid "expiration" msgstr "" -#: documents/models.py:709 +#: documents/models.py:712 msgid "slug" msgstr "" -#: documents/models.py:741 +#: documents/models.py:744 msgid "share link" msgstr "" -#: documents/models.py:742 +#: documents/models.py:745 msgid "share links" msgstr "" -#: documents/models.py:754 +#: documents/models.py:757 msgid "String" msgstr "" -#: documents/models.py:755 +#: documents/models.py:758 msgid "URL" msgstr "" -#: documents/models.py:756 +#: documents/models.py:759 msgid "Date" msgstr "" -#: documents/models.py:757 +#: documents/models.py:760 msgid "Boolean" msgstr "" -#: documents/models.py:758 +#: documents/models.py:761 msgid "Integer" msgstr "" -#: documents/models.py:759 +#: documents/models.py:762 msgid "Float" msgstr "" -#: documents/models.py:760 +#: documents/models.py:763 msgid "Monetary" msgstr "" -#: documents/models.py:761 +#: documents/models.py:764 msgid "Document Link" msgstr "" -#: documents/models.py:773 +#: documents/models.py:776 msgid "data type" msgstr "" -#: documents/models.py:781 +#: documents/models.py:784 msgid "custom field" msgstr "" -#: documents/models.py:782 +#: documents/models.py:785 msgid "custom fields" msgstr "" -#: documents/models.py:844 +#: documents/models.py:847 msgid "custom field instance" msgstr "" -#: documents/models.py:845 +#: documents/models.py:848 msgid "custom field instances" msgstr "" -#: documents/models.py:902 +#: documents/models.py:905 msgid "Consumption Started" msgstr "" -#: documents/models.py:903 +#: documents/models.py:906 msgid "Document Added" msgstr "" -#: documents/models.py:904 +#: documents/models.py:907 msgid "Document Updated" msgstr "" -#: documents/models.py:907 +#: documents/models.py:910 msgid "Consume Folder" msgstr "" -#: documents/models.py:908 +#: documents/models.py:911 msgid "Api Upload" msgstr "" -#: documents/models.py:909 +#: documents/models.py:912 msgid "Mail Fetch" msgstr "" -#: documents/models.py:912 +#: documents/models.py:915 msgid "Workflow Trigger Type" msgstr "" -#: documents/models.py:924 +#: documents/models.py:927 msgid "filter path" msgstr "" -#: documents/models.py:929 +#: documents/models.py:932 msgid "" "Only consume documents with a path that matches this if specified. Wildcards " "specified as * are allowed. Case insensitive." msgstr "" -#: documents/models.py:936 +#: documents/models.py:939 msgid "filter filename" msgstr "" -#: documents/models.py:941 paperless_mail/models.py:148 +#: documents/models.py:944 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:952 +#: documents/models.py:955 msgid "filter documents from this mail rule" msgstr "" -#: documents/models.py:968 +#: documents/models.py:971 msgid "has these tag(s)" msgstr "" -#: documents/models.py:976 +#: documents/models.py:979 msgid "has this document type" msgstr "" -#: documents/models.py:984 +#: documents/models.py:987 msgid "has this correspondent" msgstr "" -#: documents/models.py:988 +#: documents/models.py:991 msgid "workflow trigger" msgstr "" -#: documents/models.py:989 +#: documents/models.py:992 msgid "workflow triggers" msgstr "" -#: documents/models.py:997 +#: documents/models.py:1000 msgid "Assignment" msgstr "" -#: documents/models.py:1000 +#: documents/models.py:1003 msgid "Workflow Action Type" msgstr "" -#: documents/models.py:1006 +#: documents/models.py:1009 msgid "assign title" msgstr "" -#: documents/models.py:1011 +#: documents/models.py:1014 msgid "" "Assign a document title, can include some placeholders, see documentation." msgstr "" -#: documents/models.py:1019 paperless_mail/models.py:216 +#: documents/models.py:1022 paperless_mail/models.py:216 msgid "assign this tag" msgstr "" -#: documents/models.py:1027 paperless_mail/models.py:224 +#: documents/models.py:1030 paperless_mail/models.py:224 msgid "assign this document type" msgstr "" -#: documents/models.py:1035 paperless_mail/models.py:238 +#: documents/models.py:1038 paperless_mail/models.py:238 msgid "assign this correspondent" msgstr "" -#: documents/models.py:1043 +#: documents/models.py:1046 msgid "assign this storage path" msgstr "" -#: documents/models.py:1052 +#: documents/models.py:1055 msgid "assign this owner" msgstr "" -#: documents/models.py:1059 +#: documents/models.py:1062 msgid "grant view permissions to these users" msgstr "" -#: documents/models.py:1066 +#: documents/models.py:1069 msgid "grant view permissions to these groups" msgstr "" -#: documents/models.py:1073 +#: documents/models.py:1076 msgid "grant change permissions to these users" msgstr "" -#: documents/models.py:1080 +#: documents/models.py:1083 msgid "grant change permissions to these groups" msgstr "" -#: documents/models.py:1087 +#: documents/models.py:1090 msgid "assign these custom fields" msgstr "" -#: documents/models.py:1091 +#: documents/models.py:1094 msgid "workflow action" msgstr "" -#: documents/models.py:1092 +#: documents/models.py:1095 msgid "workflow actions" msgstr "" -#: documents/models.py:1101 paperless_mail/models.py:95 +#: documents/models.py:1104 paperless_mail/models.py:95 msgid "order" msgstr "" -#: documents/models.py:1107 +#: documents/models.py:1110 msgid "triggers" msgstr "" -#: documents/models.py:1114 +#: documents/models.py:1117 msgid "actions" msgstr "" -#: documents/models.py:1117 +#: documents/models.py:1120 msgid "enabled" msgstr "" -#: documents/serialisers.py:111 +#: documents/serialisers.py:112 #, python-format msgid "Invalid regular expression: %(error)s" msgstr "" -#: documents/serialisers.py:405 +#: documents/serialisers.py:406 msgid "Invalid color." msgstr "" -#: documents/serialisers.py:999 +#: documents/serialisers.py:1049 #, python-format msgid "File type %(type)s not supported" msgstr "" -#: documents/serialisers.py:1102 +#: documents/serialisers.py:1152 msgid "Invalid variable detected." msgstr "" @@ -944,267 +944,275 @@ msgstr "" msgid "Paperless" msgstr "" -#: paperless/models.py:25 +#: paperless/models.py:26 msgid "pdf" msgstr "" -#: paperless/models.py:26 +#: paperless/models.py:27 msgid "pdfa" msgstr "" -#: paperless/models.py:27 +#: paperless/models.py:28 msgid "pdfa-1" msgstr "" -#: paperless/models.py:28 +#: paperless/models.py:29 msgid "pdfa-2" msgstr "" -#: paperless/models.py:29 +#: paperless/models.py:30 msgid "pdfa-3" msgstr "" -#: paperless/models.py:38 +#: paperless/models.py:39 msgid "skip" msgstr "" -#: paperless/models.py:39 +#: paperless/models.py:40 msgid "redo" msgstr "" -#: paperless/models.py:40 +#: paperless/models.py:41 msgid "force" msgstr "" -#: paperless/models.py:41 +#: paperless/models.py:42 msgid "skip_noarchive" msgstr "" -#: paperless/models.py:49 +#: paperless/models.py:50 msgid "never" msgstr "" -#: paperless/models.py:50 +#: paperless/models.py:51 msgid "with_text" msgstr "" -#: paperless/models.py:51 +#: paperless/models.py:52 msgid "always" msgstr "" -#: paperless/models.py:59 +#: paperless/models.py:60 msgid "clean" msgstr "" -#: paperless/models.py:60 +#: paperless/models.py:61 msgid "clean-final" msgstr "" -#: paperless/models.py:61 +#: paperless/models.py:62 msgid "none" msgstr "" -#: paperless/models.py:69 +#: paperless/models.py:70 msgid "LeaveColorUnchanged" msgstr "" -#: paperless/models.py:70 +#: paperless/models.py:71 msgid "RGB" msgstr "" -#: paperless/models.py:71 +#: paperless/models.py:72 msgid "UseDeviceIndependentColor" msgstr "" -#: paperless/models.py:72 +#: paperless/models.py:73 msgid "Gray" msgstr "" -#: paperless/models.py:73 +#: paperless/models.py:74 msgid "CMYK" msgstr "" -#: paperless/models.py:82 +#: paperless/models.py:83 msgid "Sets the output PDF type" msgstr "" -#: paperless/models.py:94 +#: paperless/models.py:95 msgid "Do OCR from page 1 to this value" msgstr "" -#: paperless/models.py:100 +#: paperless/models.py:101 msgid "Do OCR using these languages" msgstr "" -#: paperless/models.py:107 +#: paperless/models.py:108 msgid "Sets the OCR mode" msgstr "" -#: paperless/models.py:115 +#: paperless/models.py:116 msgid "Controls the generation of an archive file" msgstr "" -#: paperless/models.py:123 +#: paperless/models.py:124 msgid "Sets image DPI fallback value" msgstr "" -#: paperless/models.py:130 +#: paperless/models.py:131 msgid "Controls the unpaper cleaning" msgstr "" -#: paperless/models.py:137 +#: paperless/models.py:138 msgid "Enables deskew" msgstr "" -#: paperless/models.py:140 +#: paperless/models.py:141 msgid "Enables page rotation" msgstr "" -#: paperless/models.py:145 +#: paperless/models.py:146 msgid "Sets the threshold for rotation of pages" msgstr "" -#: paperless/models.py:151 +#: paperless/models.py:152 msgid "Sets the maximum image size for decompression" msgstr "" -#: paperless/models.py:157 +#: paperless/models.py:158 msgid "Sets the Ghostscript color conversion strategy" msgstr "" -#: paperless/models.py:165 +#: paperless/models.py:166 msgid "Adds additional user arguments for OCRMyPDF" msgstr "" -#: paperless/models.py:170 +#: paperless/models.py:171 +msgid "Application title" +msgstr "" + +#: paperless/models.py:178 +msgid "Application logo" +msgstr "" + +#: paperless/models.py:188 msgid "paperless application settings" msgstr "" -#: paperless/settings.py:601 +#: paperless/settings.py:617 msgid "English (US)" msgstr "" -#: paperless/settings.py:602 +#: paperless/settings.py:618 msgid "Arabic" msgstr "" -#: paperless/settings.py:603 +#: paperless/settings.py:619 msgid "Afrikaans" msgstr "" -#: paperless/settings.py:604 +#: paperless/settings.py:620 msgid "Belarusian" msgstr "" -#: paperless/settings.py:605 +#: paperless/settings.py:621 msgid "Bulgarian" msgstr "" -#: paperless/settings.py:606 +#: paperless/settings.py:622 msgid "Catalan" msgstr "" -#: paperless/settings.py:607 +#: paperless/settings.py:623 msgid "Czech" msgstr "" -#: paperless/settings.py:608 +#: paperless/settings.py:624 msgid "Danish" msgstr "" -#: paperless/settings.py:609 +#: paperless/settings.py:625 msgid "German" msgstr "" -#: paperless/settings.py:610 +#: paperless/settings.py:626 msgid "Greek" msgstr "" -#: paperless/settings.py:611 +#: paperless/settings.py:627 msgid "English (GB)" msgstr "" -#: paperless/settings.py:612 +#: paperless/settings.py:628 msgid "Spanish" msgstr "" -#: paperless/settings.py:613 +#: paperless/settings.py:629 msgid "Finnish" msgstr "" -#: paperless/settings.py:614 +#: paperless/settings.py:630 msgid "French" msgstr "" -#: paperless/settings.py:615 +#: paperless/settings.py:631 msgid "Hungarian" msgstr "" -#: paperless/settings.py:616 +#: paperless/settings.py:632 msgid "Italian" msgstr "" -#: paperless/settings.py:617 +#: paperless/settings.py:633 msgid "Luxembourgish" msgstr "" -#: paperless/settings.py:618 +#: paperless/settings.py:634 msgid "Norwegian" msgstr "" -#: paperless/settings.py:619 +#: paperless/settings.py:635 msgid "Dutch" msgstr "" -#: paperless/settings.py:620 +#: paperless/settings.py:636 msgid "Polish" msgstr "" -#: paperless/settings.py:621 +#: paperless/settings.py:637 msgid "Portuguese (Brazil)" msgstr "" -#: paperless/settings.py:622 +#: paperless/settings.py:638 msgid "Portuguese" msgstr "" -#: paperless/settings.py:623 +#: paperless/settings.py:639 msgid "Romanian" msgstr "" -#: paperless/settings.py:624 +#: paperless/settings.py:640 msgid "Russian" msgstr "" -#: paperless/settings.py:625 +#: paperless/settings.py:641 msgid "Slovak" msgstr "" -#: paperless/settings.py:626 +#: paperless/settings.py:642 msgid "Slovenian" msgstr "" -#: paperless/settings.py:627 +#: paperless/settings.py:643 msgid "Serbian" msgstr "" -#: paperless/settings.py:628 +#: paperless/settings.py:644 msgid "Swedish" msgstr "" -#: paperless/settings.py:629 +#: paperless/settings.py:645 msgid "Turkish" msgstr "" -#: paperless/settings.py:630 +#: paperless/settings.py:646 msgid "Ukrainian" msgstr "" -#: paperless/settings.py:631 +#: paperless/settings.py:647 msgid "Chinese Simplified" msgstr "" -#: paperless/urls.py:205 +#: paperless/urls.py:214 msgid "Paperless-ngx administration" msgstr "" From 6cf732e6ec4fcba03b861e0d2b5657fc94551851 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sat, 3 Feb 2024 11:37:21 -0800 Subject: [PATCH 20/41] Add Japanese translation (#5641) --- src-ui/angular.json | 1 + src-ui/messages.xlf | 45 ++++++++++++--------- src-ui/setup-jest.ts | 2 + src-ui/src/app/app.module.ts | 2 + src-ui/src/app/services/settings.service.ts | 6 +++ src/locale/en_US/LC_MESSAGES/django.po | 34 +++++++++------- src/paperless/settings.py | 1 + 7 files changed, 57 insertions(+), 34 deletions(-) diff --git a/src-ui/angular.json b/src-ui/angular.json index ad328c072..92f15d769 100644 --- a/src-ui/angular.json +++ b/src-ui/angular.json @@ -31,6 +31,7 @@ "fr-FR": "src/locale/messages.fr_FR.xlf", "hu-HU": "src/locale/messages.hu_HU.xlf", "it-IT": "src/locale/messages.it_IT.xlf", + "ja-JP": "src/locale/messages.ja_JP.xlf", "lb-LU": "src/locale/messages.lb_LU.xlf", "nl-NL": "src/locale/messages.nl_NL.xlf", "no-NO": "src/locale/messages.no_NO.xlf", diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 3bf02bf6c..9f163e3b8 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -7057,137 +7057,144 @@ 130 + + Japanese + + src/app/services/settings.service.ts + 136 + + Luxembourgish src/app/services/settings.service.ts - 136 + 142 Dutch src/app/services/settings.service.ts - 142 + 148 Norwegian src/app/services/settings.service.ts - 148 + 154 Polish src/app/services/settings.service.ts - 154 + 160 Portuguese (Brazil) src/app/services/settings.service.ts - 160 + 166 Portuguese src/app/services/settings.service.ts - 166 + 172 Romanian src/app/services/settings.service.ts - 172 + 178 Russian src/app/services/settings.service.ts - 178 + 184 Slovak src/app/services/settings.service.ts - 184 + 190 Slovenian src/app/services/settings.service.ts - 190 + 196 Serbian src/app/services/settings.service.ts - 196 + 202 Swedish src/app/services/settings.service.ts - 202 + 208 Turkish src/app/services/settings.service.ts - 208 + 214 Ukrainian src/app/services/settings.service.ts - 214 + 220 Chinese Simplified src/app/services/settings.service.ts - 220 + 226 ISO 8601 src/app/services/settings.service.ts - 228 + 234 Successfully completed one-time migratration of settings to the database! src/app/services/settings.service.ts - 471 + 477 Unable to migrate settings to the database, please try saving manually. src/app/services/settings.service.ts - 472 + 478 You can restart the tour from the settings page. src/app/services/settings.service.ts - 542 + 548 diff --git a/src-ui/setup-jest.ts b/src-ui/setup-jest.ts index 494d90d39..f2767ebf0 100644 --- a/src-ui/setup-jest.ts +++ b/src-ui/setup-jest.ts @@ -23,6 +23,7 @@ import localeFi from '@angular/common/locales/fi' import localeFr from '@angular/common/locales/fr' import localeHu from '@angular/common/locales/hu' import localeIt from '@angular/common/locales/it' +import localeJa from '@angular/common/locales/ja' import localeLb from '@angular/common/locales/lb' import localeNl from '@angular/common/locales/nl' import localeNo from '@angular/common/locales/no' @@ -53,6 +54,7 @@ registerLocaleData(localeFi) registerLocaleData(localeFr) registerLocaleData(localeHu) registerLocaleData(localeIt) +registerLocaleData(localeJa) registerLocaleData(localeLb) registerLocaleData(localeNl) registerLocaleData(localeNo) diff --git a/src-ui/src/app/app.module.ts b/src-ui/src/app/app.module.ts index 6d39bfecf..a20a69eb8 100644 --- a/src-ui/src/app/app.module.ts +++ b/src-ui/src/app/app.module.ts @@ -295,6 +295,7 @@ import localeFi from '@angular/common/locales/fi' import localeFr from '@angular/common/locales/fr' import localeHu from '@angular/common/locales/hu' import localeIt from '@angular/common/locales/it' +import localeJa from '@angular/common/locales/ja' import localeLb from '@angular/common/locales/lb' import localeNl from '@angular/common/locales/nl' import localeNo from '@angular/common/locales/no' @@ -325,6 +326,7 @@ registerLocaleData(localeFi) registerLocaleData(localeFr) registerLocaleData(localeHu) registerLocaleData(localeIt) +registerLocaleData(localeJa) registerLocaleData(localeLb) registerLocaleData(localeNl) registerLocaleData(localeNo) diff --git a/src-ui/src/app/services/settings.service.ts b/src-ui/src/app/services/settings.service.ts index 4bbeb1dde..67804fa12 100644 --- a/src-ui/src/app/services/settings.service.ts +++ b/src-ui/src/app/services/settings.service.ts @@ -131,6 +131,12 @@ const LANGUAGE_OPTIONS = [ englishName: 'Italian', dateInputFormat: 'dd/mm/yyyy', }, + { + code: 'ja-jp', + name: $localize`Japanese`, + englishName: 'Japanese', + dateInputFormat: 'yyyy/mm/dd', + }, { code: 'lb-lu', name: $localize`Luxembourgish`, diff --git a/src/locale/en_US/LC_MESSAGES/django.po b/src/locale/en_US/LC_MESSAGES/django.po index 83398f10c..0c7242462 100644 --- a/src/locale/en_US/LC_MESSAGES/django.po +++ b/src/locale/en_US/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: paperless-ngx\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-02-02 20:15-0800\n" +"POT-Creation-Date: 2024-02-02 20:17-0800\n" "PO-Revision-Date: 2022-02-17 04:17\n" "Last-Translator: \n" "Language-Team: English\n" @@ -1153,62 +1153,66 @@ msgid "Italian" msgstr "" #: paperless/settings.py:633 -msgid "Luxembourgish" +msgid "Japanese" msgstr "" #: paperless/settings.py:634 -msgid "Norwegian" +msgid "Luxembourgish" msgstr "" #: paperless/settings.py:635 -msgid "Dutch" +msgid "Norwegian" msgstr "" #: paperless/settings.py:636 -msgid "Polish" +msgid "Dutch" msgstr "" #: paperless/settings.py:637 -msgid "Portuguese (Brazil)" +msgid "Polish" msgstr "" #: paperless/settings.py:638 -msgid "Portuguese" +msgid "Portuguese (Brazil)" msgstr "" #: paperless/settings.py:639 -msgid "Romanian" +msgid "Portuguese" msgstr "" #: paperless/settings.py:640 -msgid "Russian" +msgid "Romanian" msgstr "" #: paperless/settings.py:641 -msgid "Slovak" +msgid "Russian" msgstr "" #: paperless/settings.py:642 -msgid "Slovenian" +msgid "Slovak" msgstr "" #: paperless/settings.py:643 -msgid "Serbian" +msgid "Slovenian" msgstr "" #: paperless/settings.py:644 -msgid "Swedish" +msgid "Serbian" msgstr "" #: paperless/settings.py:645 -msgid "Turkish" +msgid "Swedish" msgstr "" #: paperless/settings.py:646 -msgid "Ukrainian" +msgid "Turkish" msgstr "" #: paperless/settings.py:647 +msgid "Ukrainian" +msgstr "" + +#: paperless/settings.py:648 msgid "Chinese Simplified" msgstr "" diff --git a/src/paperless/settings.py b/src/paperless/settings.py index c9d5848c0..17ec2765d 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -630,6 +630,7 @@ LANGUAGES = [ ("fr-fr", _("French")), ("hu-hu", _("Hungarian")), ("it-it", _("Italian")), + ("ja-jp", _("Japanese")), ("lb-lu", _("Luxembourgish")), ("no-no", _("Norwegian")), ("nl-nl", _("Dutch")), From 6b34f592dfc66a9905311b94e17b8b3c08beec45 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sat, 3 Feb 2024 12:51:26 -0800 Subject: [PATCH 21/41] Fix: Explicit validation of custom field name unique constraint (#5647) --- src/documents/serialisers.py | 13 ++++++++++- src/documents/tests/test_api_custom_fields.py | 23 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 0839a14b5..2a2fc46a7 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -81,7 +81,7 @@ class MatchingModelSerializer(serializers.ModelSerializer): slug = SerializerMethodField() def validate(self, data): - # see https://github.com/encode/django-rest-framework/issues/7173 + # TODO: remove pending https://github.com/encode/django-rest-framework/issues/7173 name = data["name"] if "name" in data else self.instance.name owner = ( data["owner"] @@ -441,6 +441,17 @@ class CustomFieldSerializer(serializers.ModelSerializer): "data_type", ] + def validate(self, attrs): + # TODO: remove pending https://github.com/encode/django-rest-framework/issues/7173 + name = attrs["name"] if "name" in attrs else self.instance.name + if ("name" in attrs) and self.Meta.model.objects.filter( + name=name, + ).exists(): + raise serializers.ValidationError( + {"error": "Object violates name unique constraint"}, + ) + return super().validate(attrs) + class ReadWriteSerializerMethodField(serializers.SerializerMethodField): """ diff --git a/src/documents/tests/test_api_custom_fields.py b/src/documents/tests/test_api_custom_fields.py index cf33e2800..33124a48c 100644 --- a/src/documents/tests/test_api_custom_fields.py +++ b/src/documents/tests/test_api_custom_fields.py @@ -53,6 +53,29 @@ class TestCustomField(DirectoriesMixin, APITestCase): self.assertEqual(data["name"], name) self.assertEqual(data["data_type"], field_type) + def test_create_custom_field_nonunique_name(self): + """ + GIVEN: + - Custom field exists + WHEN: + - API request to create custom field with the same name + THEN: + - HTTP 400 is returned + """ + CustomField.objects.create( + name="Test Custom Field", + data_type=CustomField.FieldDataType.STRING, + ) + + resp = self.client.post( + self.ENDPOINT, + data={ + "data_type": "string", + "name": "Test Custom Field", + }, + ) + self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST) + def test_create_custom_field_instance(self): """ GIVEN: From 45e2b7f8149d6a6483f9bc09cb5b4130a67bcf60 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sat, 3 Feb 2024 13:22:12 -0800 Subject: [PATCH 22/41] Fix: frontend validation of number fields fails upon save (#5646) --- .../input/number/number.component.spec.ts | 31 ++++++++++--------- .../common/input/number/number.component.ts | 13 ++++++-- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src-ui/src/app/components/common/input/number/number.component.spec.ts b/src-ui/src/app/components/common/input/number/number.component.spec.ts index 9024614bb..df775a5ba 100644 --- a/src-ui/src/app/components/common/input/number/number.component.spec.ts +++ b/src-ui/src/app/components/common/input/number/number.component.spec.ts @@ -47,22 +47,25 @@ describe('NumberComponent', () => { expect(component.value).toEqual(1002) }) - it('should support float & monetary values', () => { - component.writeValue(11.13) - expect(component.value).toEqual(11) + it('should support float, monetary values & scientific notation', () => { + const mockFn = jest.fn() + component.registerOnChange(mockFn) + + component.step = 1 + component.onChange(11.13) + expect(mockFn).toHaveBeenCalledWith(11) + + component.onChange(1.23456789e8) + expect(mockFn).toHaveBeenCalledWith(123456789) + + component.step = 0.01 + component.onChange(11.1) + expect(mockFn).toHaveBeenCalledWith('11.10') + }) + + it('should display monetary values fixed to 2 decimals', () => { component.step = 0.01 component.writeValue(11.1) expect(component.value).toEqual('11.10') - component.step = 0.1 - component.writeValue(12.3456) - expect(component.value).toEqual(12.3456) - // float (step = .1) doesn't force 2 decimals - component.writeValue(11.1) - expect(component.value).toEqual(11.1) - }) - - it('should support scientific notation', () => { - component.writeValue(1.23456789e8) - expect(component.value).toEqual(123456789) }) }) diff --git a/src-ui/src/app/components/common/input/number/number.component.ts b/src-ui/src/app/components/common/input/number/number.component.ts index 6675e4498..abfd788af 100644 --- a/src-ui/src/app/components/common/input/number/number.component.ts +++ b/src-ui/src/app/components/common/input/number/number.component.ts @@ -36,9 +36,18 @@ export class NumberComponent extends AbstractInputComponent { }) } + registerOnChange(fn: any): void { + this.onChange = (newValue: any) => { + // number validation + if (this.step === 1 && newValue?.toString().indexOf('e') === -1) + newValue = parseInt(newValue, 10) + if (this.step === 0.01) newValue = parseFloat(newValue).toFixed(2) + fn(newValue) + } + } + writeValue(newValue: any): void { - if (this.step === 1 && newValue?.toString().indexOf('e') === -1) - newValue = parseInt(newValue, 10) + // Allow monetary values to be displayed with 2 decimals if (this.step === 0.01) newValue = parseFloat(newValue).toFixed(2) super.writeValue(newValue) } From 25542c56b9dd5f4f02edfb67df5c6802cbcd1345 Mon Sep 17 00:00:00 2001 From: Trenton H <797416+stumpylog@users.noreply.github.com> Date: Sun, 4 Feb 2024 10:42:21 -0800 Subject: [PATCH 23/41] Feature: Cache metadata and suggestions in Redis (#5638) --- src/documents/caching.py | 197 ++++++++++++++++++++++ src/documents/classifier.py | 20 +++ src/documents/conditionals.py | 101 ++++++++--- src/documents/tests/test_api_documents.py | 69 +++++--- src/documents/views.py | 99 +++++++---- src/paperless/settings.py | 6 +- src/setup.cfg | 1 + 7 files changed, 415 insertions(+), 78 deletions(-) create mode 100644 src/documents/caching.py diff --git a/src/documents/caching.py b/src/documents/caching.py new file mode 100644 index 000000000..9b8607dd8 --- /dev/null +++ b/src/documents/caching.py @@ -0,0 +1,197 @@ +import logging +from binascii import hexlify +from dataclasses import dataclass +from typing import TYPE_CHECKING +from typing import Final +from typing import Optional + +from django.core.cache import cache + +from documents.models import Document + +if TYPE_CHECKING: + from documents.classifier import DocumentClassifier + +logger = logging.getLogger("paperless.caching") + + +@dataclass(frozen=True) +class MetadataCacheData: + original_checksum: str + original_metadata: list + archive_checksum: Optional[str] + archive_metadata: Optional[list] + + +@dataclass(frozen=True) +class SuggestionCacheData: + classifier_version: int + classifier_hash: str + suggestions: dict + + +CLASSIFIER_VERSION_KEY: Final[str] = "classifier_version" +CLASSIFIER_HASH_KEY: Final[str] = "classifier_hash" +CLASSIFIER_MODIFIED_KEY: Final[str] = "classifier_modified" + +CACHE_1_MINUTE: Final[int] = 60 +CACHE_5_MINUTES: Final[int] = 5 * CACHE_1_MINUTE +CACHE_50_MINUTES: Final[int] = 50 * CACHE_1_MINUTE + + +def get_suggestion_cache_key(document_id: int) -> str: + """ + Returns the basic key for a document's suggestions + """ + return f"doc_{document_id}_suggest" + + +def get_suggestion_cache(document_id: int) -> Optional[SuggestionCacheData]: + """ + If possible, return the cached suggestions for the given document ID. + The classifier needs to be matching in format and hash and the suggestions need to + have been cached once. + """ + from documents.classifier import DocumentClassifier + + doc_key = get_suggestion_cache_key(document_id) + cache_hits = cache.get_many([CLASSIFIER_VERSION_KEY, CLASSIFIER_HASH_KEY, doc_key]) + # The document suggestions are in the cache + if doc_key in cache_hits: + doc_suggestions: SuggestionCacheData = cache_hits[doc_key] + # The classifier format is the same + # The classifier hash is the same + # Then the suggestions can be used + if ( + CLASSIFIER_VERSION_KEY in cache_hits + and cache_hits[CLASSIFIER_VERSION_KEY] == DocumentClassifier.FORMAT_VERSION + and cache_hits[CLASSIFIER_VERSION_KEY] == doc_suggestions.classifier_version + ) and ( + CLASSIFIER_HASH_KEY in cache_hits + and cache_hits[CLASSIFIER_HASH_KEY] == doc_suggestions.classifier_hash + ): + return doc_suggestions + else: # pragma: no cover + # Remove the key because something didn't match + cache.delete(doc_key) + return None + + +def set_suggestions_cache( + document_id: int, + suggestions: dict, + classifier: Optional["DocumentClassifier"], + *, + timeout=CACHE_50_MINUTES, +) -> None: + """ + Caches the given suggestions, which were generated by the given classifier. If there is no classifier, + this function is a no-op (there won't be suggestions then anyway) + """ + if classifier is not None: + doc_key = get_suggestion_cache_key(document_id) + print(classifier.last_auto_type_hash) + cache.set( + doc_key, + SuggestionCacheData( + classifier.FORMAT_VERSION, + hexlify(classifier.last_auto_type_hash).decode(), + suggestions, + ), + timeout, + ) + + +def refresh_suggestions_cache( + document_id: int, + *, + timeout: int = CACHE_50_MINUTES, +) -> None: + """ + Refreshes the expiration of the suggestions for the given document ID + to the given timeout + """ + doc_key = get_suggestion_cache_key(document_id) + cache.touch(doc_key, timeout) + + +def get_metadata_cache_key(document_id: int) -> str: + """ + Returns the basic key for a document's metadata + """ + return f"doc_{document_id}_metadata" + + +def get_metadata_cache(document_id: int) -> Optional[MetadataCacheData]: + """ + Returns the cached document metadata for the given document ID, as long as the metadata + was cached once and the checksums have not changed + """ + doc_key = get_metadata_cache_key(document_id) + doc_metadata: Optional[MetadataCacheData] = cache.get(doc_key) + # The metadata exists in the cache + if doc_metadata is not None: + try: + doc = Document.objects.get(pk=document_id) + # The original checksums match + # If it has one, the archive checksums match + # Then, we can use the metadata + if ( + doc_metadata.original_checksum == doc.checksum + and doc.has_archive_version + and doc_metadata.archive_checksum is not None + and doc_metadata.archive_checksum == doc.archive_checksum + ): + # Refresh cache + cache.touch(doc_key, CACHE_50_MINUTES) + return doc_metadata + else: # pragma: no cover + # Something didn't match, delete the key + cache.delete(doc_key) + except Document.DoesNotExist: # pragma: no cover + # Basically impossible, but the key existed, but the Document didn't + cache.delete(doc_key) + return None + + +def set_metadata_cache( + document: Document, + original_metadata: list, + archive_metadata: Optional[list], + *, + timeout=CACHE_50_MINUTES, +) -> None: + """ + Sets the metadata into cache for the given Document + """ + doc_key = get_metadata_cache_key(document.pk) + cache.set( + doc_key, + MetadataCacheData( + document.checksum, + original_metadata, + document.archive_checksum, + archive_metadata, + ), + timeout, + ) + + +def refresh_metadata_cache( + document_id: int, + *, + timeout: int = CACHE_50_MINUTES, +) -> None: + """ + Refreshes the expiration of the metadata for the given document ID + to the given timeout + """ + doc_key = get_metadata_cache_key(document_id) + cache.touch(doc_key, timeout) + + +def get_thumbnail_modified_key(document_id: int) -> str: + """ + Builds the key to store a thumbnail's timestamp + """ + return f"doc_{document_id}_thumbnail_modified" diff --git a/src/documents/classifier.py b/src/documents/classifier.py index 5833e373e..6180a8671 100644 --- a/src/documents/classifier.py +++ b/src/documents/classifier.py @@ -10,8 +10,13 @@ from pathlib import Path from typing import Optional from django.conf import settings +from django.core.cache import cache from sklearn.exceptions import InconsistentVersionWarning +from documents.caching import CACHE_50_MINUTES +from documents.caching import CLASSIFIER_HASH_KEY +from documents.caching import CLASSIFIER_MODIFIED_KEY +from documents.caching import CLASSIFIER_VERSION_KEY from documents.models import Document from documents.models import MatchingModel @@ -208,6 +213,15 @@ class DocumentClassifier: and self.last_doc_change_time >= latest_doc_change ) and self.last_auto_type_hash == hasher.digest(): logger.info("No updates since last training") + # Set the classifier information into the cache + # Caching for 50 minutes, so slightly less than the normal retrain time + cache.set( + CLASSIFIER_MODIFIED_KEY, + self.last_doc_change_time, + CACHE_50_MINUTES, + ) + cache.set(CLASSIFIER_HASH_KEY, hasher.hexdigest(), CACHE_50_MINUTES) + cache.set(CLASSIFIER_VERSION_KEY, self.FORMAT_VERSION, CACHE_50_MINUTES) return False # subtract 1 since -1 (null) is also part of the classes. @@ -322,6 +336,12 @@ class DocumentClassifier: self.last_doc_change_time = latest_doc_change self.last_auto_type_hash = hasher.digest() + # Set the classifier information into the cache + # Caching for 50 minutes, so slightly less than the normal retrain time + cache.set(CLASSIFIER_MODIFIED_KEY, self.last_doc_change_time, CACHE_50_MINUTES) + cache.set(CLASSIFIER_HASH_KEY, hasher.hexdigest(), CACHE_50_MINUTES) + cache.set(CLASSIFIER_VERSION_KEY, self.FORMAT_VERSION, CACHE_50_MINUTES) + return True def preprocess_content(self, content: str) -> str: # pragma: no cover diff --git a/src/documents/conditionals.py b/src/documents/conditionals.py index 07e6850fb..1b53dfe2b 100644 --- a/src/documents/conditionals.py +++ b/src/documents/conditionals.py @@ -1,9 +1,16 @@ -import pickle from datetime import datetime +from datetime import timezone from typing import Optional from django.conf import settings +from django.core.cache import cache +from documents.caching import CACHE_5_MINUTES +from documents.caching import CACHE_50_MINUTES +from documents.caching import CLASSIFIER_HASH_KEY +from documents.caching import CLASSIFIER_MODIFIED_KEY +from documents.caching import CLASSIFIER_VERSION_KEY +from documents.caching import get_thumbnail_modified_key from documents.classifier import DocumentClassifier from documents.models import Document @@ -14,18 +21,25 @@ def suggestions_etag(request, pk: int) -> Optional[str]: suggestions if the classifier has not been changed and the suggested dates setting is also unchanged - TODO: It would be nice to not duplicate the partial loading and the loading - between here and the actual classifier """ + # If no model file, no etag at all if not settings.MODEL_FILE.exists(): return None - with open(settings.MODEL_FILE, "rb") as f: - schema_version = pickle.load(f) - if schema_version != DocumentClassifier.FORMAT_VERSION: - return None - _ = pickle.load(f) - last_auto_type_hash: bytes = pickle.load(f) - return f"{last_auto_type_hash}:{settings.NUMBER_OF_SUGGESTED_DATES}" + # Check cache information + cache_hits = cache.get_many( + [CLASSIFIER_VERSION_KEY, CLASSIFIER_HASH_KEY], + ) + # If the version differs somehow, no etag + if ( + CLASSIFIER_VERSION_KEY in cache_hits + and cache_hits[CLASSIFIER_VERSION_KEY] != DocumentClassifier.FORMAT_VERSION + ): + return None + elif CLASSIFIER_HASH_KEY in cache_hits: + # Refresh the cache and return the hash digest and the dates setting + cache.touch(CLASSIFIER_HASH_KEY, CACHE_5_MINUTES) + return f"{cache_hits[CLASSIFIER_HASH_KEY]}:{settings.NUMBER_OF_SUGGESTED_DATES}" + return None def suggestions_last_modified(request, pk: int) -> Optional[datetime]: @@ -34,14 +48,23 @@ def suggestions_last_modified(request, pk: int) -> Optional[datetime]: as there is not way to track the suggested date setting modification, but it seems unlikely that changes too often """ + # No file, no last modified if not settings.MODEL_FILE.exists(): return None - with open(settings.MODEL_FILE, "rb") as f: - schema_version = pickle.load(f) - if schema_version != DocumentClassifier.FORMAT_VERSION: - return None - last_doc_change_time = pickle.load(f) - return last_doc_change_time + cache_hits = cache.get_many( + [CLASSIFIER_VERSION_KEY, CLASSIFIER_MODIFIED_KEY], + ) + # If the version differs somehow, no last modified + if ( + CLASSIFIER_VERSION_KEY in cache_hits + and cache_hits[CLASSIFIER_VERSION_KEY] != DocumentClassifier.FORMAT_VERSION + ): + return None + elif CLASSIFIER_MODIFIED_KEY in cache_hits: + # Refresh the cache and return the last modified + cache.touch(CLASSIFIER_MODIFIED_KEY, CACHE_5_MINUTES) + return cache_hits[CLASSIFIER_MODIFIED_KEY] + return None def metadata_etag(request, pk: int) -> Optional[str]: @@ -52,7 +75,7 @@ def metadata_etag(request, pk: int) -> Optional[str]: try: doc = Document.objects.get(pk=pk) return doc.checksum - except Document.DoesNotExist: + except Document.DoesNotExist: # pragma: no cover return None return None @@ -66,7 +89,7 @@ def metadata_last_modified(request, pk: int) -> Optional[datetime]: try: doc = Document.objects.get(pk=pk) return doc.modified - except Document.DoesNotExist: + except Document.DoesNotExist: # pragma: no cover return None return None @@ -82,6 +105,46 @@ def preview_etag(request, pk: int) -> Optional[str]: and request.query_params["original"] == "true" ) return doc.checksum if use_original else doc.archive_checksum - except Document.DoesNotExist: + except Document.DoesNotExist: # pragma: no cover return None return None + + +def preview_last_modified(request, pk: int) -> Optional[datetime]: + """ + Uses the documents modified time to set the Last-Modified header. Not strictly + speaking correct, but close enough and quick + """ + try: + doc = Document.objects.get(pk=pk) + return doc.modified + except Document.DoesNotExist: # pragma: no cover + return None + return None + + +def thumbnail_last_modified(request, pk: int) -> Optional[datetime]: + """ + Returns the filesystem last modified either from cache or from filesystem. + Cache should be (slightly?) faster than filesystem + """ + try: + doc = Document.objects.get(pk=pk) + if not doc.thumbnail_path.exists(): + return None + doc_key = get_thumbnail_modified_key(pk) + + cache_hit = cache.get(doc_key) + if cache_hit is not None: + cache.touch(doc_key, CACHE_50_MINUTES) + return cache_hit + + # No cache, get the timestamp and cache the datetime + last_modified = datetime.fromtimestamp( + doc.thumbnail_path.stat().st_mtime, + tz=timezone.utc, + ) + cache.set(doc_key, last_modified, CACHE_50_MINUTES) + return last_modified + except Document.DoesNotExist: # pragma: no cover + return None diff --git a/src/documents/tests/test_api_documents.py b/src/documents/tests/test_api_documents.py index 20dd64d82..d7ae1eeb7 100644 --- a/src/documents/tests/test_api_documents.py +++ b/src/documents/tests/test_api_documents.py @@ -4,6 +4,7 @@ import shutil import tempfile import uuid import zoneinfo +from binascii import hexlify from datetime import timedelta from pathlib import Path from unittest import mock @@ -13,12 +14,17 @@ from dateutil import parser from django.conf import settings from django.contrib.auth.models import Permission from django.contrib.auth.models import User +from django.core.cache import cache from django.test import override_settings from django.utils import timezone from guardian.shortcuts import assign_perm from rest_framework import status from rest_framework.test import APITestCase +from documents.caching import CACHE_50_MINUTES +from documents.caching import CLASSIFIER_HASH_KEY +from documents.caching import CLASSIFIER_MODIFIED_KEY +from documents.caching import CLASSIFIER_VERSION_KEY from documents.models import Correspondent from documents.models import CustomField from documents.models import CustomFieldInstance @@ -40,6 +46,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): self.user = User.objects.create_superuser(username="temp_admin") self.client.force_authenticate(user=self.user) + cache.clear() def testDocuments(self): response = self.client.get("/api/documents/").data @@ -1162,6 +1169,9 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): self.assertEqual(meta["original_size"], os.stat(source_file).st_size) self.assertEqual(meta["archive_size"], os.stat(archive_file).st_size) + response = self.client.get(f"/api/documents/{doc.pk}/metadata/") + self.assertEqual(response.status_code, status.HTTP_200_OK) + def test_get_metadata_invalid_doc(self): response = self.client.get("/api/documents/34576/metadata/") self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) @@ -1266,7 +1276,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): }, ) - @mock.patch("documents.conditionals.pickle.load") + @mock.patch("documents.views.load_classifier") @mock.patch("documents.views.match_storage_paths") @mock.patch("documents.views.match_document_types") @mock.patch("documents.views.match_tags") @@ -1278,7 +1288,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): match_tags, match_document_types, match_storage_paths, - mocked_pickle_load, + mocked_load, ): """ GIVEN: @@ -1287,23 +1297,43 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): - Classifier has not been modified THEN: - Subsequent requests are returned alright - - ETag and last modified are called + - ETag and last modified headers are set """ - settings.MODEL_FILE.touch() + # setup the cache how the classifier does it from documents.classifier import DocumentClassifier - last_modified = timezone.now() + settings.MODEL_FILE.touch() - # ETag first, then modified - mock_effect = [ - DocumentClassifier.FORMAT_VERSION, - "dont care", - b"thisisachecksum", - DocumentClassifier.FORMAT_VERSION, - last_modified, + classifier_checksum_bytes = b"thisisachecksum" + classifier_checksum_hex = hexlify(classifier_checksum_bytes).decode() + + # Two loads, so two side effects + mocked_load.side_effect = [ + mock.Mock( + last_auto_type_hash=classifier_checksum_bytes, + FORMAT_VERSION=DocumentClassifier.FORMAT_VERSION, + ), + mock.Mock( + last_auto_type_hash=classifier_checksum_bytes, + FORMAT_VERSION=DocumentClassifier.FORMAT_VERSION, + ), ] - mocked_pickle_load.side_effect = mock_effect + + last_modified = timezone.now() + cache.set(CLASSIFIER_MODIFIED_KEY, last_modified, CACHE_50_MINUTES) + cache.set(CLASSIFIER_HASH_KEY, classifier_checksum_hex, CACHE_50_MINUTES) + cache.set( + CLASSIFIER_VERSION_KEY, + DocumentClassifier.FORMAT_VERSION, + CACHE_50_MINUTES, + ) + + # Mock the matching + match_correspondents.return_value = [Correspondent(id=88), Correspondent(id=2)] + match_tags.return_value = [Tag(id=56), Tag(id=123)] + match_document_types.return_value = [DocumentType(id=23)] + match_storage_paths.return_value = [StoragePath(id=99), StoragePath(id=77)] doc = Document.objects.create( title="test", @@ -1311,12 +1341,8 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): content="this is an invoice from 12.04.2022!", ) - match_correspondents.return_value = [Correspondent(id=88), Correspondent(id=2)] - match_tags.return_value = [Tag(id=56), Tag(id=123)] - match_document_types.return_value = [DocumentType(id=23)] - match_storage_paths.return_value = [StoragePath(id=99), StoragePath(id=77)] - response = self.client.get(f"/api/documents/{doc.pk}/suggestions/") + self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual( response.data, { @@ -1327,7 +1353,6 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): "dates": ["2022-04-12"], }, ) - mocked_pickle_load.assert_called() self.assertIn("Last-Modified", response.headers) self.assertEqual( response.headers["Last-Modified"], @@ -1336,15 +1361,11 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): self.assertIn("ETag", response.headers) self.assertEqual( response.headers["ETag"], - f"\"b'thisisachecksum':{settings.NUMBER_OF_SUGGESTED_DATES}\"", + f'"{classifier_checksum_hex}:{settings.NUMBER_OF_SUGGESTED_DATES}"', ) - mocked_pickle_load.rest_mock() - mocked_pickle_load.side_effect = mock_effect - response = self.client.get(f"/api/documents/{doc.pk}/suggestions/") self.assertEqual(response.status_code, status.HTTP_200_OK) - mocked_pickle_load.assert_called() @mock.patch("documents.parsers.parse_date_generator") @override_settings(NUMBER_OF_SUGGESTED_DATES=0) diff --git a/src/documents/views.py b/src/documents/views.py index 11fb5b1f2..0578cdb24 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -35,6 +35,7 @@ from django.utils.translation import get_language from django.views import View from django.views.decorators.cache import cache_control from django.views.decorators.http import condition +from django.views.decorators.http import last_modified from django.views.generic import TemplateView from django_filters.rest_framework import DjangoFilterBackend from langdetect import detect @@ -62,12 +63,21 @@ from documents import bulk_edit from documents.bulk_download import ArchiveOnlyStrategy from documents.bulk_download import OriginalAndArchiveStrategy from documents.bulk_download import OriginalsOnlyStrategy +from documents.caching import CACHE_50_MINUTES +from documents.caching import get_metadata_cache +from documents.caching import get_suggestion_cache +from documents.caching import refresh_metadata_cache +from documents.caching import refresh_suggestions_cache +from documents.caching import set_metadata_cache +from documents.caching import set_suggestions_cache from documents.classifier import load_classifier from documents.conditionals import metadata_etag from documents.conditionals import metadata_last_modified from documents.conditionals import preview_etag +from documents.conditionals import preview_last_modified from documents.conditionals import suggestions_etag from documents.conditionals import suggestions_last_modified +from documents.conditionals import thumbnail_last_modified from documents.data_models import ConsumableDocument from documents.data_models import DocumentMetadataOverrides from documents.data_models import DocumentSource @@ -379,10 +389,12 @@ class DocumentViewSet( try: return parser.extract_metadata(file, mime_type) - except Exception: + except Exception: # pragma: no cover + logger.exception(f"Issue getting metadata for {file}") # TODO: cover GPG errors, remove later. return [] - else: + else: # pragma: no cover + logger.warning(f"No parser for {mime_type}") return [] def get_filesize(self, filename): @@ -407,16 +419,37 @@ class DocumentViewSet( except Document.DoesNotExist: raise Http404 + document_cached_metadata = get_metadata_cache(doc.pk) + + archive_metadata = None + archive_filesize = None + if document_cached_metadata is not None: + original_metadata = document_cached_metadata.original_metadata + archive_metadata = document_cached_metadata.archive_metadata + refresh_metadata_cache(doc.pk) + else: + original_metadata = self.get_metadata(doc.source_path, doc.mime_type) + + if doc.has_archive_version: + archive_filesize = self.get_filesize(doc.archive_path) + archive_metadata = self.get_metadata( + doc.archive_path, + "application/pdf", + ) + set_metadata_cache(doc, original_metadata, archive_metadata) + meta = { "original_checksum": doc.checksum, "original_size": self.get_filesize(doc.source_path), "original_mime_type": doc.mime_type, "media_filename": doc.filename, "has_archive_version": doc.has_archive_version, - "original_metadata": self.get_metadata(doc.source_path, doc.mime_type), + "original_metadata": original_metadata, "archive_checksum": doc.archive_checksum, "archive_media_filename": doc.archive_filename, "original_filename": doc.original_filename, + "archive_size": archive_filesize, + "archive_metadata": archive_metadata, } lang = "en" @@ -426,16 +459,6 @@ class DocumentViewSet( pass meta["lang"] = lang - if doc.has_archive_version: - meta["archive_size"] = self.get_filesize(doc.archive_path) - meta["archive_metadata"] = self.get_metadata( - doc.archive_path, - "application/pdf", - ) - else: - meta["archive_size"] = None - meta["archive_metadata"] = None - return Response(meta) @action(methods=["get"], detail=True) @@ -454,6 +477,12 @@ class DocumentViewSet( ): return HttpResponseForbidden("Insufficient permissions") + document_suggestions = get_suggestion_cache(doc.pk) + + if document_suggestions is not None: + refresh_suggestions_cache(doc.pk) + return Response(document_suggestions.suggestions) + classifier = load_classifier() dates = [] @@ -463,27 +492,30 @@ class DocumentViewSet( {i for i in itertools.islice(gen, settings.NUMBER_OF_SUGGESTED_DATES)}, ) - return Response( - { - "correspondents": [ - c.id for c in match_correspondents(doc, classifier, request.user) - ], - "tags": [t.id for t in match_tags(doc, classifier, request.user)], - "document_types": [ - dt.id for dt in match_document_types(doc, classifier, request.user) - ], - "storage_paths": [ - dt.id for dt in match_storage_paths(doc, classifier, request.user) - ], - "dates": [ - date.strftime("%Y-%m-%d") for date in dates if date is not None - ], - }, - ) + resp_data = { + "correspondents": [ + c.id for c in match_correspondents(doc, classifier, request.user) + ], + "tags": [t.id for t in match_tags(doc, classifier, request.user)], + "document_types": [ + dt.id for dt in match_document_types(doc, classifier, request.user) + ], + "storage_paths": [ + dt.id for dt in match_storage_paths(doc, classifier, request.user) + ], + "dates": [date.strftime("%Y-%m-%d") for date in dates if date is not None], + } + + # Cache the suggestions and the classifier hash for later + set_suggestions_cache(doc.pk, resp_data, classifier) + + return Response(resp_data) @action(methods=["get"], detail=True) @method_decorator(cache_control(public=False, max_age=5 * 60)) - @method_decorator(condition(etag_func=preview_etag)) + @method_decorator( + condition(etag_func=preview_etag, last_modified_func=preview_last_modified), + ) def preview(self, request, pk=None): try: response = self.file_response(pk, request, "inline") @@ -492,7 +524,8 @@ class DocumentViewSet( raise Http404 @action(methods=["get"], detail=True) - @method_decorator(cache_control(public=False, max_age=315360000)) + @method_decorator(cache_control(public=False, max_age=CACHE_50_MINUTES)) + @method_decorator(last_modified(thumbnail_last_modified)) def thumb(self, request, pk=None): try: doc = Document.objects.get(id=pk) @@ -506,8 +539,6 @@ class DocumentViewSet( handle = GnuPG.decrypted(doc.thumbnail_file) else: handle = doc.thumbnail_file - # TODO: Send ETag information and use that to send new thumbnails - # if available return HttpResponse(handle, content_type="image/webp") except (FileNotFoundError, Document.DoesNotExist): diff --git a/src/paperless/settings.py b/src/paperless/settings.py index 17ec2765d..7179f0358 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -762,8 +762,12 @@ CELERY_BEAT_SCHEDULE_FILENAME = os.path.join(DATA_DIR, "celerybeat-schedule.db") # django setting. CACHES = { "default": { - "BACKEND": "django.core.cache.backends.redis.RedisCache", + "BACKEND": os.environ.get( + "PAPERLESS_CACHE_BACKEND", + "django.core.cache.backends.redis.RedisCache", + ), "LOCATION": _CHANNELS_REDIS_URL, + "KEY_PREFIX": os.getenv("PAPERLESS_REDIS_PREFIX", ""), }, } diff --git a/src/setup.cfg b/src/setup.cfg index dc5e9e33a..1877cb16e 100644 --- a/src/setup.cfg +++ b/src/setup.cfg @@ -3,6 +3,7 @@ DJANGO_SETTINGS_MODULE = paperless.settings addopts = --pythonwarnings=all --cov --cov-report=html --cov-report=xml --numprocesses auto --maxprocesses=16 --quiet --durations=50 env = PAPERLESS_DISABLE_DBHANDLER=true + PAPERLESS_CACHE_BACKEND=django.core.cache.backends.locmem.LocMemCache [coverage:run] source = From 625780899d67deaf64f81173184fbf8dc30da788 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sun, 4 Feb 2024 12:39:26 -0800 Subject: [PATCH 24/41] Fix location of bulk edit tests --- src/documents/{ => tests}/test_bulk_edit.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/documents/{ => tests}/test_bulk_edit.py (100%) diff --git a/src/documents/test_bulk_edit.py b/src/documents/tests/test_bulk_edit.py similarity index 100% rename from src/documents/test_bulk_edit.py rename to src/documents/tests/test_bulk_edit.py From c7e0c32226969a3983891ab55bc0e3f51a8794e4 Mon Sep 17 00:00:00 2001 From: LarsBel <39060393+LarsBel@users.noreply.github.com> Date: Sun, 4 Feb 2024 23:57:24 +0100 Subject: [PATCH 25/41] Documentation: Include example redis URI with database index (#5653) Co-Authored-By: shamoon <4887959+shamoon@users.noreply.github.com> --- docs/configuration.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/configuration.md b/docs/configuration.md index 6dc341346..f473921cb 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -34,6 +34,8 @@ matcher. `redis://:@:` - With the requirepass option PAPERLESS_REDIS = `redis://:@:` + - To include the redis database index PAPERLESS_REDIS = + `redis://:@:/` [More information on securing your Redis Instance](https://redis.io/docs/getting-started/#securing-redis). From fb82aa0ee1b84b8b5fab04116061d2ec4a5fa90b Mon Sep 17 00:00:00 2001 From: pkrahmer <5699756+pkrahmer@users.noreply.github.com> Date: Mon, 5 Feb 2024 18:38:19 +0100 Subject: [PATCH 26/41] Feature: Allow tagging by putting barcode stickers on documents (#5580) --- docs/configuration.md | 49 +++++++++++ paperless.conf.example | 2 + src/documents/barcodes.py | 62 +++++++++++++- src/documents/tests/test_barcodes.py | 123 +++++++++++++++++++++++++++ src/paperless/settings.py | 13 +++ 5 files changed, 248 insertions(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index f473921cb..e99e0a085 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1173,6 +1173,55 @@ combination with PAPERLESS_CONSUMER_BARCODE_UPSCALE bigger than 1.0. Defaults to "300" +#### [`PAPERLESS_CONSUMER_ENABLE_TAG_BARCODE=`](#PAPERLESS_CONSUMER_ENABLE_TAG_BARCODE) {#PAPERLESS_CONSUMER_ENABLE_TAG_BARCODE} + +: Enables the detection of barcodes in the scanned document and +assigns or creates tags if a properly formatted barcode is detected. + + The barcode must match one of the (configurable) regular expressions. + If the barcode text contains ',' (comma), it is split into multiple + barcodes which are individually processed for tagging. + + Matching is case insensitive. + + Defaults to false. + +#### [`PAPERLESS_CONSUMER_TAG_BARCODE_MAPPING=`](#PAPERLESS_CONSUMER_TAG_BARCODE_MAPPING) {#PAPERLESS_CONSUMER_TAG_BARCODE_MAPPING} + +: Defines a dictionary of filter regex and substitute expressions. + + Syntax: {"": "" [,...]]} + + A barcode is considered for tagging if the barcode text matches + at least one of the provided pattern. + + If a match is found, the rule is applied. This allows very + versatile reformatting and mapping of barcode pattern to tag values. + + If a tag is not found it will be created. + + Defaults to: + + {"TAG:(.*)": "\\g<1>"} which defines + - a regex TAG:(.*) which includes barcodes beginning with TAG: + followed by any text that gets stored into match group #1 and + - a substitute \\g<1> that replaces the original barcode text + by the content in match group #1. + Consequently, the tag is the barcode text without its TAG: prefix. + + More examples: + + {"ASN12.*": "JOHN", "ASN13.*": "SMITH"} for example maps + - ASN12nnnn barcodes to the tag JOHN and + - ASN13nnnn barcodes to the tag SMITH. + + {"T-J": "JOHN", "T-S": "SMITH", "T-D": "DOE"} directly maps + - T-J barcodes to the tag JOHN, + - T-S barcodes to the tag SMITH and + - T-D barcodes to the tag DOE. + + Please refer to the Python regex documentation for more information. + ## Audit Trail #### [`PAPERLESS_AUDIT_LOG_ENABLED=`](#PAPERLESS_AUDIT_LOG_ENABLED) {#PAPERLESS_AUDIT_LOG_ENABLED} diff --git a/paperless.conf.example b/paperless.conf.example index 1610dcda9..db557a7b6 100644 --- a/paperless.conf.example +++ b/paperless.conf.example @@ -68,6 +68,8 @@ #PAPERLESS_CONSUMER_BARCODE_STRING=PATCHT #PAPERLESS_CONSUMER_BARCODE_UPSCALE=0.0 #PAPERLESS_CONSUMER_BARCODE_DPI=300 +#PAPERLESS_CONSUMER_ENABLE_TAG_BARCODE=false +#PAPERLESS_CONSUMER_TAG_BARCODE_MAPPING={"TAG:(.*)": "\\g<1>"} #PAPERLESS_CONSUMER_ENABLE_COLLATE_DOUBLE_SIDED=false #PAPERLESS_CONSUMER_COLLATE_DOUBLE_SIDED_SUBDIR_NAME=double-sided #PAPERLESS_CONSUMER_COLLATE_DOUBLE_SIDED_TIFF_SUPPORT=false diff --git a/src/documents/barcodes.py b/src/documents/barcodes.py index 606451f84..4bfb9b791 100644 --- a/src/documents/barcodes.py +++ b/src/documents/barcodes.py @@ -14,6 +14,7 @@ from PIL import Image from documents.converters import convert_from_tiff_to_pdf from documents.data_models import ConsumableDocument +from documents.models import Tag from documents.plugins.base import ConsumeTaskPlugin from documents.plugins.base import StopConsumeTaskError from documents.plugins.helpers import ProgressStatusOptions @@ -65,7 +66,9 @@ class BarcodePlugin(ConsumeTaskPlugin): supported_mimes = {"application/pdf"} return ( - settings.CONSUMER_ENABLE_ASN_BARCODE or settings.CONSUMER_ENABLE_BARCODES + settings.CONSUMER_ENABLE_ASN_BARCODE + or settings.CONSUMER_ENABLE_BARCODES + or settings.CONSUMER_ENABLE_TAG_BARCODE ) and self.input_doc.mime_type in supported_mimes def setup(self): @@ -90,6 +93,16 @@ class BarcodePlugin(ConsumeTaskPlugin): logger.info(f"Found ASN in barcode: {located_asn}") self.metadata.asn = located_asn + # try reading tags from barcodes + if settings.CONSUMER_ENABLE_TAG_BARCODE: + tags = self.tags + if tags is not None and len(tags) > 0: + if self.metadata.tag_ids: + self.metadata.tag_ids += tags + else: + self.metadata.tag_ids = tags + logger.info(f"Found tags in barcode: {tags}") + separator_pages = self.get_separation_pages() if not separator_pages: return "No pages to split on!" @@ -279,6 +292,53 @@ class BarcodePlugin(ConsumeTaskPlugin): return asn + @property + def tags(self) -> Optional[list[int]]: + """ + Search the parsed barcodes for any tags. + Returns the detected tag ids (or empty list) + """ + tags = [] + + # Ensure the barcodes have been read + self.detect() + + for x in self.barcodes: + tag_texts = x.value + + for raw in tag_texts.split(","): + try: + tag = None + for regex in settings.CONSUMER_TAG_BARCODE_MAPPING: + if re.match(regex, raw, flags=re.IGNORECASE): + sub = settings.CONSUMER_TAG_BARCODE_MAPPING[regex] + tag = ( + re.sub(regex, sub, raw, flags=re.IGNORECASE) + if sub + else raw + ) + break + + if tag: + tag = Tag.objects.get_or_create( + name__iexact=tag, + defaults={"name": tag}, + )[0] + + logger.debug( + f"Found Tag Barcode '{raw}', substituted " + f"to '{tag}' and mapped to " + f"tag #{tag.pk}.", + ) + tags.append(tag.pk) + + except Exception as e: + logger.error( + f"Failed to find or create TAG '{raw}' because: {e}", + ) + + return tags + def get_separation_pages(self) -> dict[int, bool]: """ Search the parsed barcodes for separators and returns a dict of page diff --git a/src/documents/tests/test_barcodes.py b/src/documents/tests/test_barcodes.py index 4552a2b77..3dd6d62ff 100644 --- a/src/documents/tests/test_barcodes.py +++ b/src/documents/tests/test_barcodes.py @@ -14,6 +14,7 @@ from documents.barcodes import BarcodePlugin from documents.data_models import ConsumableDocument from documents.data_models import DocumentMetadataOverrides from documents.data_models import DocumentSource +from documents.models import Tag from documents.tests.utils import DirectoriesMixin from documents.tests.utils import DocumentConsumeDelayMixin from documents.tests.utils import DummyProgressManager @@ -741,3 +742,125 @@ class TestBarcodeZxing(TestBarcode): @override_settings(CONSUMER_BARCODE_SCANNER="ZXING") class TestAsnBarcodesZxing(TestAsnBarcode): pass + + +class TestTagBarcode(DirectoriesMixin, SampleDirMixin, GetReaderPluginMixin, TestCase): + @contextmanager + def get_reader(self, filepath: Path) -> BarcodePlugin: + reader = BarcodePlugin( + ConsumableDocument(DocumentSource.ConsumeFolder, original_file=filepath), + DocumentMetadataOverrides(), + DummyProgressManager(filepath.name, None), + self.dirs.scratch_dir, + "task-id", + ) + reader.setup() + yield reader + reader.cleanup() + + @override_settings(CONSUMER_ENABLE_TAG_BARCODE=True) + def test_scan_file_without_matching_barcodes(self): + """ + GIVEN: + - PDF containing tag barcodes but none with matching prefix (default "TAG:") + WHEN: + - File is scanned for barcodes + THEN: + - No TAG has been created + """ + test_file = self.BARCODE_SAMPLE_DIR / "barcode-39-asn-custom-prefix.pdf" + with self.get_reader(test_file) as reader: + reader.run() + tags = reader.metadata.tag_ids + self.assertEqual(tags, None) + + @override_settings( + CONSUMER_ENABLE_TAG_BARCODE=False, + CONSUMER_TAG_BARCODE_MAPPING={"CUSTOM-PREFIX-(.*)": "\\g<1>"}, + ) + def test_scan_file_with_matching_barcode_but_function_disabled(self): + """ + GIVEN: + - PDF containing a tag barcode with matching custom prefix + - The tag barcode functionality is disabled + WHEN: + - File is scanned for barcodes + THEN: + - No TAG has been created + """ + test_file = self.BARCODE_SAMPLE_DIR / "barcode-39-asn-custom-prefix.pdf" + with self.get_reader(test_file) as reader: + reader.run() + tags = reader.metadata.tag_ids + self.assertEqual(tags, None) + + @override_settings( + CONSUMER_ENABLE_TAG_BARCODE=True, + CONSUMER_TAG_BARCODE_MAPPING={"CUSTOM-PREFIX-(.*)": "\\g<1>"}, + ) + def test_scan_file_for_tag_custom_prefix(self): + """ + GIVEN: + - PDF containing a tag barcode with custom prefix + - The barcode mapping accepts this prefix and removes it from the mapped tag value + - The created tag is the non-prefixed values + WHEN: + - File is scanned for barcodes + THEN: + - The TAG is located + - One TAG has been created + """ + test_file = self.BARCODE_SAMPLE_DIR / "barcode-39-asn-custom-prefix.pdf" + with self.get_reader(test_file) as reader: + reader.metadata.tag_ids = [99] + reader.run() + self.assertEqual(reader.pdf_file, test_file) + tags = reader.metadata.tag_ids + self.assertEqual(len(tags), 2) + self.assertEqual(tags[0], 99) + self.assertEqual(Tag.objects.get(name__iexact="00123").pk, tags[1]) + + @override_settings( + CONSUMER_ENABLE_TAG_BARCODE=True, + CONSUMER_TAG_BARCODE_MAPPING={"ASN(.*)": "\\g<1>"}, + ) + def test_scan_file_for_many_custom_tags(self): + """ + GIVEN: + - PDF containing multiple tag barcode with custom prefix + - The barcode mapping accepts this prefix and removes it from the mapped tag value + - The created tags are the non-prefixed values + WHEN: + - File is scanned for barcodes + THEN: + - The TAG is located + - File Tags have been created + """ + test_file = self.BARCODE_SAMPLE_DIR / "split-by-asn-1.pdf" + with self.get_reader(test_file) as reader: + reader.run() + tags = reader.metadata.tag_ids + self.assertEqual(len(tags), 5) + self.assertEqual(Tag.objects.get(name__iexact="00123").pk, tags[0]) + self.assertEqual(Tag.objects.get(name__iexact="00124").pk, tags[1]) + self.assertEqual(Tag.objects.get(name__iexact="00125").pk, tags[2]) + self.assertEqual(Tag.objects.get(name__iexact="00126").pk, tags[3]) + self.assertEqual(Tag.objects.get(name__iexact="00127").pk, tags[4]) + + @override_settings( + CONSUMER_ENABLE_TAG_BARCODE=True, + CONSUMER_TAG_BARCODE_MAPPING={"CUSTOM-PREFIX-(.*)": "\\g<3>"}, + ) + def test_scan_file_for_tag_raises_value_error(self): + """ + GIVEN: + - Any error occurs during tag barcode processing + THEN: + - The processing should be skipped and not break the import + """ + test_file = self.BARCODE_SAMPLE_DIR / "barcode-39-asn-custom-prefix.pdf" + with self.get_reader(test_file) as reader: + reader.run() + # expect error to be caught and logged only + tags = reader.metadata.tag_ids + self.assertEqual(tags, None) diff --git a/src/paperless/settings.py b/src/paperless/settings.py index 7179f0358..4f7894acc 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -853,6 +853,19 @@ CONSUMER_BARCODE_UPSCALE: Final[float] = __get_float( CONSUMER_BARCODE_DPI: Final[int] = __get_int("PAPERLESS_CONSUMER_BARCODE_DPI", 300) +CONSUMER_ENABLE_TAG_BARCODE: Final[bool] = __get_boolean( + "PAPERLESS_CONSUMER_ENABLE_TAG_BARCODE", +) + +CONSUMER_TAG_BARCODE_MAPPING = dict( + json.loads( + os.getenv( + "PAPERLESS_CONSUMER_TAG_BARCODE_MAPPING", + '{"TAG:(.*)": "\\\\g<1>"}', + ), + ), +) + CONSUMER_ENABLE_COLLATE_DOUBLE_SIDED: Final[bool] = __get_boolean( "PAPERLESS_CONSUMER_ENABLE_COLLATE_DOUBLE_SIDED", ) From 4813a7bc70d800a96538ebc5313bff6b57470c5d Mon Sep 17 00:00:00 2001 From: Trenton H <797416+stumpylog@users.noreply.github.com> Date: Mon, 5 Feb 2024 13:46:59 -0800 Subject: [PATCH 27/41] Chore: Adds additional rules for Ruff linter (#5660) --- .ruff.toml | 34 ++++- src/documents/caching.py | 1 - src/documents/classifier.py | 7 +- .../management/commands/document_retagger.py | 14 +- .../commands/document_thumbnails.py | 2 +- src/documents/plugins/helpers.py | 4 +- src/documents/signals/handlers.py | 130 ++++++++---------- src/documents/tests/test_api_bulk_download.py | 2 - .../tests/test_management_consumer.py | 8 +- src/documents/tests/test_management_fuzzy.py | 2 +- src/documents/tests/test_workflows.py | 5 +- src/documents/tests/utils.py | 1 - src/paperless/auth.py | 6 +- 13 files changed, 117 insertions(+), 99 deletions(-) diff --git a/.ruff.toml b/.ruff.toml index c69de0ee1..497d3f1eb 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -1,7 +1,29 @@ -# https://beta.ruff.rs/docs/settings/ -# https://beta.ruff.rs/docs/rules/ -extend-select = ["I", "W", "UP", "COM", "DJ", "EXE", "ISC", "ICN", "G201", "INP", "PIE", "RSE", "SIM", "TID", "PLC", "PLE", "RUF"] -# TODO PTH +# https://docs.astral.sh/ruff/settings/ +# https://docs.astral.sh/ruff/rules/ +extend-select = [ + "W", # https://docs.astral.sh/ruff/rules/#pycodestyle-e-w + "I", # https://docs.astral.sh/ruff/rules/#isort-i + "UP", # https://docs.astral.sh/ruff/rules/#pyupgrade-up + "COM", # https://docs.astral.sh/ruff/rules/#flake8-commas-com + "DJ", # https://docs.astral.sh/ruff/rules/#flake8-django-dj + "EXE", # https://docs.astral.sh/ruff/rules/#flake8-executable-exe + "ISC", # https://docs.astral.sh/ruff/rules/#flake8-implicit-str-concat-isc + "ICN", # https://docs.astral.sh/ruff/rules/#flake8-import-conventions-icn + "G201", # https://docs.astral.sh/ruff/rules/#flake8-logging-format-g + "INP", # https://docs.astral.sh/ruff/rules/#flake8-no-pep420-inp + "PIE", # https://docs.astral.sh/ruff/rules/#flake8-pie-pie + "Q", # https://docs.astral.sh/ruff/rules/#flake8-quotes-q + "RSE", # https://docs.astral.sh/ruff/rules/#flake8-raise-rse + "T20", # https://docs.astral.sh/ruff/rules/#flake8-print-t20 + "SIM", # https://docs.astral.sh/ruff/rules/#flake8-simplify-sim + "TID", # https://docs.astral.sh/ruff/rules/#flake8-tidy-imports-tid + "TCH", # https://docs.astral.sh/ruff/rules/#flake8-type-checking-tch + "PLC", # https://docs.astral.sh/ruff/rules/#pylint-pl + "PLE", # https://docs.astral.sh/ruff/rules/#pylint-pl + "RUF", # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf + "FLY", # https://docs.astral.sh/ruff/rules/#flynt-fly +] +# TODO PTH https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth ignore = ["DJ001", "SIM105", "RUF012"] fix = true line-length = 88 @@ -13,9 +35,9 @@ show-fixes = true [per-file-ignores] ".github/scripts/*.py" = ["E501", "INP001", "SIM117"] -"docker/wait-for-redis.py" = ["INP001"] +"docker/wait-for-redis.py" = ["INP001", "T201"] "*/tests/*.py" = ["E501", "SIM117"] -"*/migrations/*.py" = ["E501", "SIM"] +"*/migrations/*.py" = ["E501", "SIM", "T201"] "src/paperless_tesseract/tests/test_parser.py" = ["RUF001"] "src/documents/models.py" = ["SIM115"] diff --git a/src/documents/caching.py b/src/documents/caching.py index 9b8607dd8..d80f319f7 100644 --- a/src/documents/caching.py +++ b/src/documents/caching.py @@ -90,7 +90,6 @@ def set_suggestions_cache( """ if classifier is not None: doc_key = get_suggestion_cache_key(document_id) - print(classifier.last_auto_type_hash) cache.set( doc_key, SuggestionCacheData( diff --git a/src/documents/classifier.py b/src/documents/classifier.py index 6180a8671..aa0eb70b6 100644 --- a/src/documents/classifier.py +++ b/src/documents/classifier.py @@ -4,11 +4,14 @@ import pickle import re import warnings from collections.abc import Iterator -from datetime import datetime from hashlib import sha256 -from pathlib import Path +from typing import TYPE_CHECKING from typing import Optional +if TYPE_CHECKING: + from datetime import datetime + from pathlib import Path + from django.conf import settings from django.core.cache import cache from sklearn.exceptions import InconsistentVersionWarning diff --git a/src/documents/management/commands/document_retagger.py b/src/documents/management/commands/document_retagger.py index dda3ecebc..10bb54b71 100644 --- a/src/documents/management/commands/document_retagger.py +++ b/src/documents/management/commands/document_retagger.py @@ -69,8 +69,6 @@ class Command(ProgressBarMixin, BaseCommand): def handle(self, *args, **options): self.handle_progress_bar_mixin(**options) - # Detect if we support color - color = self.style.ERROR("test") != "test" if options["inbox_only"]: queryset = Document.objects.filter(tags__is_inbox_tag=True) @@ -96,7 +94,8 @@ class Command(ProgressBarMixin, BaseCommand): use_first=options["use_first"], suggest=options["suggest"], base_url=options["base_url"], - color=color, + stdout=self.stdout, + style_func=self.style, ) if options["document_type"]: @@ -108,7 +107,8 @@ class Command(ProgressBarMixin, BaseCommand): use_first=options["use_first"], suggest=options["suggest"], base_url=options["base_url"], - color=color, + stdout=self.stdout, + style_func=self.style, ) if options["tags"]: @@ -119,7 +119,8 @@ class Command(ProgressBarMixin, BaseCommand): replace=options["overwrite"], suggest=options["suggest"], base_url=options["base_url"], - color=color, + stdout=self.stdout, + style_func=self.style, ) if options["storage_path"]: set_storage_path( @@ -130,5 +131,6 @@ class Command(ProgressBarMixin, BaseCommand): use_first=options["use_first"], suggest=options["suggest"], base_url=options["base_url"], - color=color, + stdout=self.stdout, + style_func=self.style, ) diff --git a/src/documents/management/commands/document_thumbnails.py b/src/documents/management/commands/document_thumbnails.py index ecd265102..d4653f0b3 100644 --- a/src/documents/management/commands/document_thumbnails.py +++ b/src/documents/management/commands/document_thumbnails.py @@ -19,7 +19,7 @@ def _process_document(doc_id): if parser_class: parser = parser_class(logging_group=None) else: - print(f"{document} No parser for mime type {document.mime_type}") + print(f"{document} No parser for mime type {document.mime_type}") # noqa: T201 return try: diff --git a/src/documents/plugins/helpers.py b/src/documents/plugins/helpers.py index 92fe1255b..27d03f30f 100644 --- a/src/documents/plugins/helpers.py +++ b/src/documents/plugins/helpers.py @@ -5,7 +5,9 @@ from typing import Union from asgiref.sync import async_to_sync from channels.layers import get_channel_layer -from channels_redis.pubsub import RedisPubSubChannelLayer + +if TYPE_CHECKING: + from channels_redis.pubsub import RedisPubSubChannelLayer class ProgressStatusOptions(str, enum.Enum): diff --git a/src/documents/signals/handlers.py b/src/documents/signals/handlers.py index 2b717e042..c8657ce1d 100644 --- a/src/documents/signals/handlers.py +++ b/src/documents/signals/handlers.py @@ -18,7 +18,6 @@ from django.db import close_old_connections from django.db import models from django.db.models import Q from django.dispatch import receiver -from django.utils import termcolors from django.utils import timezone from filelock import FileLock @@ -54,6 +53,26 @@ def add_inbox_tags(sender, document: Document, logging_group=None, **kwargs): document.tags.add(*inbox_tags) +def _suggestion_printer( + stdout, + style_func, + suggestion_type: str, + document: Document, + selected: MatchingModel, + base_url: Optional[str] = None, +): + """ + Smaller helper to reduce duplication when just outputting suggestions to the console + """ + doc_str = str(document) + if base_url is not None: + stdout.write(style_func.SUCCESS(doc_str)) + stdout.write(style_func.SUCCESS(f"{base_url}/documents/{document.pk}")) + else: + stdout.write(style_func.SUCCESS(f"{doc_str} [{document.pk}]")) + stdout.write(f"Suggest {suggestion_type}: {selected}") + + def set_correspondent( sender, document: Document, @@ -63,7 +82,8 @@ def set_correspondent( use_first=True, suggest=False, base_url=None, - color=False, + stdout=None, + style_func=None, **kwargs, ): if document.correspondent and not replace: @@ -90,23 +110,14 @@ def set_correspondent( if selected or replace: if suggest: - if base_url: - print( - termcolors.colorize(str(document), fg="green") - if color - else str(document), - ) - print(f"{base_url}/documents/{document.pk}") - else: - print( - ( - termcolors.colorize(str(document), fg="green") - if color - else str(document) - ) - + f" [{document.pk}]", - ) - print(f"Suggest correspondent {selected}") + _suggestion_printer( + stdout, + style_func, + "correspondent", + document, + selected, + base_url, + ) else: logger.info( f"Assigning correspondent {selected} to {document}", @@ -126,7 +137,8 @@ def set_document_type( use_first=True, suggest=False, base_url=None, - color=False, + stdout=None, + style_func=None, **kwargs, ): if document.document_type and not replace: @@ -154,23 +166,14 @@ def set_document_type( if selected or replace: if suggest: - if base_url: - print( - termcolors.colorize(str(document), fg="green") - if color - else str(document), - ) - print(f"{base_url}/documents/{document.pk}") - else: - print( - ( - termcolors.colorize(str(document), fg="green") - if color - else str(document) - ) - + f" [{document.pk}]", - ) - print(f"Suggest document type {selected}") + _suggestion_printer( + stdout, + style_func, + "document type", + document, + selected, + base_url, + ) else: logger.info( f"Assigning document type {selected} to {document}", @@ -189,7 +192,8 @@ def set_tags( replace=False, suggest=False, base_url=None, - color=False, + stdout=None, + style_func=None, **kwargs, ): if replace: @@ -212,26 +216,16 @@ def set_tags( ] if not relevant_tags and not extra_tags: return + doc_str = style_func.SUCCESS(str(document)) if base_url: - print( - termcolors.colorize(str(document), fg="green") - if color - else str(document), - ) - print(f"{base_url}/documents/{document.pk}") + stdout.write(doc_str) + stdout.write(f"{base_url}/documents/{document.pk}") else: - print( - ( - termcolors.colorize(str(document), fg="green") - if color - else str(document) - ) - + f" [{document.pk}]", - ) + stdout.write(doc_str + style_func.SUCCESS(f" [{document.pk}]")) if relevant_tags: - print("Suggest tags: " + ", ".join([t.name for t in relevant_tags])) + stdout.write("Suggest tags: " + ", ".join([t.name for t in relevant_tags])) if extra_tags: - print("Extra tags: " + ", ".join([t.name for t in extra_tags])) + stdout.write("Extra tags: " + ", ".join([t.name for t in extra_tags])) else: if not relevant_tags: return @@ -254,7 +248,8 @@ def set_storage_path( use_first=True, suggest=False, base_url=None, - color=False, + stdout=None, + style_func=None, **kwargs, ): if document.storage_path and not replace: @@ -285,23 +280,14 @@ def set_storage_path( if selected or replace: if suggest: - if base_url: - print( - termcolors.colorize(str(document), fg="green") - if color - else str(document), - ) - print(f"{base_url}/documents/{document.pk}") - else: - print( - ( - termcolors.colorize(str(document), fg="green") - if color - else str(document) - ) - + f" [{document.pk}]", - ) - print(f"Suggest storage directory {selected}") + _suggestion_printer( + stdout, + style_func, + "storage directory", + document, + selected, + base_url, + ) else: logger.info( f"Assigning storage path {selected} to {document}", diff --git a/src/documents/tests/test_api_bulk_download.py b/src/documents/tests/test_api_bulk_download.py index 57912c65c..43299b77d 100644 --- a/src/documents/tests/test_api_bulk_download.py +++ b/src/documents/tests/test_api_bulk_download.py @@ -246,8 +246,6 @@ class TestBulkDownload(DirectoriesMixin, APITestCase): self.doc3.title = "Title 2 - Doc 3" self.doc3.save() - print(self.doc3.archive_path) - print(self.doc3.archive_filename) response = self.client.post( self.ENDPOINT, diff --git a/src/documents/tests/test_management_consumer.py b/src/documents/tests/test_management_consumer.py index 99d5d410e..7e2707403 100644 --- a/src/documents/tests/test_management_consumer.py +++ b/src/documents/tests/test_management_consumer.py @@ -88,10 +88,10 @@ class ConsumerThreadMixin(DocumentConsumeDelayMixin): ): eq = filecmp.cmp(input_doc.original_file, self.sample_file, shallow=False) if not eq: - print("Consumed an INVALID file.") + print("Consumed an INVALID file.") # noqa: T201 raise ConsumerError("Incomplete File READ FAILED") else: - print("Consumed a perfectly valid file.") + print("Consumed a perfectly valid file.") # noqa: T201 def slow_write_file(self, target, incomplete=False): with open(self.sample_file, "rb") as f: @@ -102,11 +102,11 @@ class ConsumerThreadMixin(DocumentConsumeDelayMixin): with open(target, "wb") as f: # this will take 2 seconds, since the file is about 20k. - print("Start writing file.") + print("Start writing file.") # noqa: T201 for b in chunked(1000, pdf_bytes): f.write(b) sleep(0.1) - print("file completed.") + print("file completed.") # noqa: T201 @override_settings( diff --git a/src/documents/tests/test_management_fuzzy.py b/src/documents/tests/test_management_fuzzy.py index c215c43ca..7cc1f265e 100644 --- a/src/documents/tests/test_management_fuzzy.py +++ b/src/documents/tests/test_management_fuzzy.py @@ -196,7 +196,7 @@ class TestFuzzyMatchCommand(TestCase): self.assertEqual(Document.objects.count(), 3) stdout, _ = self.call_command("--delete") - print(stdout) + lines = [x.strip() for x in stdout.split("\n") if len(x.strip())] self.assertEqual(len(lines), 3) self.assertEqual( diff --git a/src/documents/tests/test_workflows.py b/src/documents/tests/test_workflows.py index ba5c53a78..95f903239 100644 --- a/src/documents/tests/test_workflows.py +++ b/src/documents/tests/test_workflows.py @@ -1,16 +1,19 @@ from datetime import timedelta from pathlib import Path +from typing import TYPE_CHECKING from unittest import mock from django.contrib.auth.models import Group from django.contrib.auth.models import User -from django.db.models import QuerySet from django.utils import timezone from guardian.shortcuts import assign_perm from guardian.shortcuts import get_groups_with_perms from guardian.shortcuts import get_users_with_perms from rest_framework.test import APITestCase +if TYPE_CHECKING: + from django.db.models import QuerySet + from documents import tasks from documents.data_models import ConsumableDocument from documents.data_models import DocumentSource diff --git a/src/documents/tests/utils.py b/src/documents/tests/utils.py index 4c3305d13..ba435d5c3 100644 --- a/src/documents/tests/utils.py +++ b/src/documents/tests/utils.py @@ -340,7 +340,6 @@ class DummyProgressManager: def __init__(self, filename: str, task_id: Optional[str] = None) -> None: self.filename = filename self.task_id = task_id - print("hello world") self.payloads = [] def __enter__(self): diff --git a/src/paperless/auth.py b/src/paperless/auth.py index 98e2a8b30..ba9320b5d 100644 --- a/src/paperless/auth.py +++ b/src/paperless/auth.py @@ -1,3 +1,5 @@ +import logging + from django.conf import settings from django.contrib import auth from django.contrib.auth.middleware import PersistentRemoteUserMiddleware @@ -6,6 +8,8 @@ from django.http import HttpRequest from django.utils.deprecation import MiddlewareMixin from rest_framework import authentication +logger = logging.getLogger("paperless.auth") + class AutoLoginMiddleware(MiddlewareMixin): def process_request(self, request: HttpRequest): @@ -35,7 +39,7 @@ class AngularApiAuthenticationOverride(authentication.BaseAuthentication): and request.headers["Referer"].startswith("http://localhost:4200/") ): user = User.objects.filter(is_staff=True).first() - print(f"Auto-Login with user {user}") + logger.debug(f"Auto-Login with user {user}") return (user, None) else: return None From 4606caeaa8785c16077094e526232a7a56b69e71 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Tue, 6 Feb 2024 07:16:15 -0800 Subject: [PATCH 28/41] Chore: Use memory cache backend in debug mode (#5666) --- src/paperless/settings.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/paperless/settings.py b/src/paperless/settings.py index 4f7894acc..d485415ca 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -771,6 +771,11 @@ CACHES = { }, } +if DEBUG and os.getenv("PAPERLESS_CACHE_BACKEND") is None: + CACHES["default"][ + "BACKEND" + ] = "django.core.cache.backends.locmem.LocMemCache" # pragma: no cover + def default_threads_per_worker(task_workers) -> int: # always leave one core open From aaa130e20de4f8fd178055c34b360b7fb11f0aa0 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Tue, 6 Feb 2024 07:31:07 -0800 Subject: [PATCH 29/41] Feature: allow create objects from bulk edit (#5667) --- .../filterable-dropdown.component.html | 16 +- .../filterable-dropdown.component.spec.ts | 42 ++++ .../filterable-dropdown.component.ts | 23 ++- .../bulk-editor/bulk-editor.component.html | 4 + .../bulk-editor/bulk-editor.component.spec.ts | 194 ++++++++++++++++++ .../bulk-editor/bulk-editor.component.ts | 93 ++++++++- 6 files changed, 364 insertions(+), 8 deletions(-) diff --git a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.html b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.html index 599faa988..cac217716 100644 --- a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.html +++ b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.html @@ -45,10 +45,18 @@
} @if (editing) { - + @if ((selectionModel.itemsSorted | filter: filterText).length === 0 && createRef !== undefined) { + + } + @if ((selectionModel.itemsSorted | filter: filterText).length > 0) { + + } } @if (!editing && manyToOne) {
diff --git a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.spec.ts b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.spec.ts index f88667f34..58aa029ee 100644 --- a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.spec.ts +++ b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.spec.ts @@ -500,4 +500,46 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () => selectionModel.apply() expect(selectionModel.itemsSorted).toEqual([nullItem, items[1], items[0]]) }) + + it('should set support create, keep open model and call createRef method', fakeAsync(() => { + component.items = items + component.icon = 'tag-fill' + component.selectionModel = selectionModel + fixture.nativeElement + .querySelector('button') + .dispatchEvent(new MouseEvent('click')) // open + fixture.detectChanges() + tick(100) + + component.filterText = 'Test Filter Text' + component.createRef = jest.fn() + component.createClicked() + expect(component.creating).toBeTruthy() + expect(component.createRef).toHaveBeenCalledWith('Test Filter Text') + const openSpy = jest.spyOn(component.dropdown, 'open') + component.dropdownOpenChange(false) + expect(openSpy).toHaveBeenCalled() // should keep open + })) + + it('should call create on enter inside filter field if 0 items remain while editing', fakeAsync(() => { + component.items = items + component.icon = 'tag-fill' + component.editing = true + component.createRef = jest.fn() + const createSpy = jest.spyOn(component, 'createClicked') + expect(component.selectionModel.getSelectedItems()).toEqual([]) + fixture.nativeElement + .querySelector('button') + .dispatchEvent(new MouseEvent('click')) // open + fixture.detectChanges() + tick(100) + component.filterText = 'FooBar' + fixture.detectChanges() + component.listFilterTextInput.nativeElement.dispatchEvent( + new KeyboardEvent('keyup', { key: 'Enter' }) + ) + expect(component.selectionModel.getSelectedItems()).toEqual([]) + tick(300) + expect(createSpy).toHaveBeenCalled() + })) }) diff --git a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts index 26b036db9..bb1a9da27 100644 --- a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts +++ b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts @@ -398,6 +398,11 @@ export class FilterableDropdownComponent { @Input() disabled = false + @Input() + createRef: (name) => void + + creating: boolean = false + @Output() apply = new EventEmitter() @@ -437,6 +442,11 @@ export class FilterableDropdownComponent { } } + createClicked() { + this.creating = true + this.createRef(this.filterText) + } + dropdownOpenChange(open: boolean): void { if (open) { setTimeout(() => { @@ -448,9 +458,14 @@ export class FilterableDropdownComponent { } this.opened.next(this) } else { - this.filterText = '' - if (this.applyOnClose && this.selectionModel.isDirty()) { - this.apply.emit(this.selectionModel.diff()) + if (this.creating) { + this.dropdown.open() + this.creating = false + } else { + this.filterText = '' + if (this.applyOnClose && this.selectionModel.isDirty()) { + this.apply.emit(this.selectionModel.diff()) + } } } } @@ -466,6 +481,8 @@ export class FilterableDropdownComponent { this.dropdown.close() } }, 200) + } else if (filtered.length == 0 && this.createRef) { + this.createClicked() } } diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.html b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.html index 0c261df67..686c07bb3 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.html +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.html @@ -25,6 +25,7 @@ [editing]="true" [manyToOne]="true" [applyOnClose]="applyOnClose" + [createRef]="createTag.bind(this)" (opened)="openTagsDropdown()" [(selectionModel)]="tagSelectionModel" [documentCounts]="tagDocumentCounts" @@ -38,6 +39,7 @@ [disabled]="!userCanEditAll" [editing]="true" [applyOnClose]="applyOnClose" + [createRef]="createCorrespondent.bind(this)" (opened)="openCorrespondentDropdown()" [(selectionModel)]="correspondentSelectionModel" [documentCounts]="correspondentDocumentCounts" @@ -51,6 +53,7 @@ [disabled]="!userCanEditAll" [editing]="true" [applyOnClose]="applyOnClose" + [createRef]="createDocumentType.bind(this)" (opened)="openDocumentTypeDropdown()" [(selectionModel)]="documentTypeSelectionModel" [documentCounts]="documentTypeDocumentCounts" @@ -64,6 +67,7 @@ [disabled]="!userCanEditAll" [editing]="true" [applyOnClose]="applyOnClose" + [createRef]="createStoragePath.bind(this)" (opened)="openStoragePathDropdown()" [(selectionModel)]="storagePathsSelectionModel" [documentCounts]="storagePathDocumentCounts" diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts index 42f8b6d1d..4da9f36df 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts @@ -42,6 +42,16 @@ import { NgSelectModule } from '@ng-select/ng-select' import { GroupService } from 'src/app/services/rest/group.service' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' import { SwitchComponent } from '../../common/input/switch/switch.component' +import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component' +import { TagEditDialogComponent } from '../../common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component' +import { Results } from 'src/app/data/results' +import { Tag } from 'src/app/data/tag' +import { Correspondent } from 'src/app/data/correspondent' +import { DocumentType } from 'src/app/data/document-type' +import { StoragePath } from 'src/app/data/storage-path' +import { CorrespondentEditDialogComponent } from '../../common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component' +import { DocumentTypeEditDialogComponent } from '../../common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component' +import { StoragePathEditDialogComponent } from '../../common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component' const selectionData: SelectionData = { selected_tags: [ @@ -65,6 +75,10 @@ describe('BulkEditorComponent', () => { let documentService: DocumentService let toastService: ToastService let modalService: NgbModal + let tagService: TagService + let correspondentsService: CorrespondentService + let documentTypeService: DocumentTypeService + let storagePathService: StoragePathService let httpTestingController: HttpTestingController beforeEach(async () => { @@ -165,6 +179,10 @@ describe('BulkEditorComponent', () => { documentService = TestBed.inject(DocumentService) toastService = TestBed.inject(ToastService) modalService = TestBed.inject(NgbModal) + tagService = TestBed.inject(TagService) + correspondentsService = TestBed.inject(CorrespondentService) + documentTypeService = TestBed.inject(DocumentTypeService) + storagePathService = TestBed.inject(StoragePathService) httpTestingController = TestBed.inject(HttpTestingController) fixture = TestBed.createComponent(BulkEditorComponent) @@ -902,4 +920,180 @@ describe('BulkEditorComponent', () => { `${environment.apiBaseUrl}documents/storage_paths/` ) }) + + it('should support create new tag', () => { + const name = 'New Tag' + const newTag = { id: 101, name: 'New Tag' } + const tags: Results = { + results: [ + { id: 1, name: 'Tag 1' }, + { id: 2, name: 'Tag 2' }, + ], + count: 2, + all: [1, 2], + } + + const modalInstance = { + componentInstance: { + dialogMode: EditDialogMode.CREATE, + object: { name }, + succeeded: of(newTag), + }, + } + const tagListAllSpy = jest.spyOn(tagService, 'listAll') + tagListAllSpy.mockReturnValue(of(tags)) + + const tagSelectionModelToggleSpy = jest.spyOn( + component.tagSelectionModel, + 'toggle' + ) + + const modalServiceOpenSpy = jest.spyOn(modalService, 'open') + modalServiceOpenSpy.mockReturnValue(modalInstance as any) + + component.createTag(name) + + expect(modalServiceOpenSpy).toHaveBeenCalledWith(TagEditDialogComponent, { + backdrop: 'static', + }) + expect(tagListAllSpy).toHaveBeenCalled() + + expect(tagSelectionModelToggleSpy).toHaveBeenCalledWith(newTag.id) + expect(component.tags).toEqual(tags.results) + }) + + it('should support create new correspondent', () => { + const name = 'New Correspondent' + const newCorrespondent = { id: 101, name: 'New Correspondent' } + const correspondents: Results = { + results: [ + { id: 1, name: 'Correspondent 1' }, + { id: 2, name: 'Correspondent 2' }, + ], + count: 2, + all: [1, 2], + } + + const modalInstance = { + componentInstance: { + dialogMode: EditDialogMode.CREATE, + object: { name }, + succeeded: of(newCorrespondent), + }, + } + const correspondentsListAllSpy = jest.spyOn( + correspondentsService, + 'listAll' + ) + correspondentsListAllSpy.mockReturnValue(of(correspondents)) + + const correspondentSelectionModelToggleSpy = jest.spyOn( + component.correspondentSelectionModel, + 'toggle' + ) + + const modalServiceOpenSpy = jest.spyOn(modalService, 'open') + modalServiceOpenSpy.mockReturnValue(modalInstance as any) + + component.createCorrespondent(name) + + expect(modalServiceOpenSpy).toHaveBeenCalledWith( + CorrespondentEditDialogComponent, + { backdrop: 'static' } + ) + expect(correspondentsListAllSpy).toHaveBeenCalled() + + expect(correspondentSelectionModelToggleSpy).toHaveBeenCalledWith( + newCorrespondent.id + ) + expect(component.correspondents).toEqual(correspondents.results) + }) + + it('should support create new document type', () => { + const name = 'New Document Type' + const newDocumentType = { id: 101, name: 'New Document Type' } + const documentTypes: Results = { + results: [ + { id: 1, name: 'Document Type 1' }, + { id: 2, name: 'Document Type 2' }, + ], + count: 2, + all: [1, 2], + } + + const modalInstance = { + componentInstance: { + dialogMode: EditDialogMode.CREATE, + object: { name }, + succeeded: of(newDocumentType), + }, + } + const documentTypesListAllSpy = jest.spyOn(documentTypeService, 'listAll') + documentTypesListAllSpy.mockReturnValue(of(documentTypes)) + + const documentTypeSelectionModelToggleSpy = jest.spyOn( + component.documentTypeSelectionModel, + 'toggle' + ) + + const modalServiceOpenSpy = jest.spyOn(modalService, 'open') + modalServiceOpenSpy.mockReturnValue(modalInstance as any) + + component.createDocumentType(name) + + expect(modalServiceOpenSpy).toHaveBeenCalledWith( + DocumentTypeEditDialogComponent, + { backdrop: 'static' } + ) + expect(documentTypesListAllSpy).toHaveBeenCalled() + + expect(documentTypeSelectionModelToggleSpy).toHaveBeenCalledWith( + newDocumentType.id + ) + expect(component.documentTypes).toEqual(documentTypes.results) + }) + + it('should support create new storage path', () => { + const name = 'New Storage Path' + const newStoragePath = { id: 101, name: 'New Storage Path' } + const storagePaths: Results = { + results: [ + { id: 1, name: 'Storage Path 1' }, + { id: 2, name: 'Storage Path 2' }, + ], + count: 2, + all: [1, 2], + } + + const modalInstance = { + componentInstance: { + dialogMode: EditDialogMode.CREATE, + object: { name }, + succeeded: of(newStoragePath), + }, + } + const storagePathsListAllSpy = jest.spyOn(storagePathService, 'listAll') + storagePathsListAllSpy.mockReturnValue(of(storagePaths)) + + const storagePathsSelectionModelToggleSpy = jest.spyOn( + component.storagePathsSelectionModel, + 'toggle' + ) + + const modalServiceOpenSpy = jest.spyOn(modalService, 'open') + modalServiceOpenSpy.mockReturnValue(modalInstance as any) + + component.createStoragePath(name) + + expect(modalServiceOpenSpy).toHaveBeenCalledWith( + StoragePathEditDialogComponent, + { backdrop: 'static' } + ) + expect(storagePathsListAllSpy).toHaveBeenCalled() + + expect(storagePathsSelectionModelToggleSpy).toHaveBeenCalledWith( + newStoragePath.id + ) + expect(component.storagePaths).toEqual(storagePaths.results) + }) }) diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts index 49d4c070f..0bfb287cb 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -33,7 +33,12 @@ import { PermissionType, } from 'src/app/services/permissions.service' import { FormControl, FormGroup } from '@angular/forms' -import { first, Subject, takeUntil } from 'rxjs' +import { first, map, Subject, switchMap, takeUntil } from 'rxjs' +import { CorrespondentEditDialogComponent } from '../../common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component' +import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component' +import { TagEditDialogComponent } from '../../common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component' +import { DocumentTypeEditDialogComponent } from '../../common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component' +import { StoragePathEditDialogComponent } from '../../common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component' @Component({ selector: 'pngx-bulk-editor', @@ -479,6 +484,92 @@ export class BulkEditorComponent } } + createTag(name: string) { + let modal = this.modalService.open(TagEditDialogComponent, { + backdrop: 'static', + }) + modal.componentInstance.dialogMode = EditDialogMode.CREATE + modal.componentInstance.object = { name } + modal.componentInstance.succeeded + .pipe( + switchMap((newTag) => { + return this.tagService + .listAll() + .pipe(map((tags) => ({ newTag, tags }))) + }) + ) + .pipe(takeUntil(this.unsubscribeNotifier)) + .subscribe(({ newTag, tags }) => { + this.tags = tags.results + this.tagSelectionModel.toggle(newTag.id) + }) + } + + createCorrespondent(name: string) { + let modal = this.modalService.open(CorrespondentEditDialogComponent, { + backdrop: 'static', + }) + modal.componentInstance.dialogMode = EditDialogMode.CREATE + modal.componentInstance.object = { name } + modal.componentInstance.succeeded + .pipe( + switchMap((newCorrespondent) => { + return this.correspondentService + .listAll() + .pipe( + map((correspondents) => ({ newCorrespondent, correspondents })) + ) + }) + ) + .pipe(takeUntil(this.unsubscribeNotifier)) + .subscribe(({ newCorrespondent, correspondents }) => { + this.correspondents = correspondents.results + this.correspondentSelectionModel.toggle(newCorrespondent.id) + }) + } + + createDocumentType(name: string) { + let modal = this.modalService.open(DocumentTypeEditDialogComponent, { + backdrop: 'static', + }) + modal.componentInstance.dialogMode = EditDialogMode.CREATE + modal.componentInstance.object = { name } + modal.componentInstance.succeeded + .pipe( + switchMap((newDocumentType) => { + return this.documentTypeService + .listAll() + .pipe(map((documentTypes) => ({ newDocumentType, documentTypes }))) + }) + ) + .pipe(takeUntil(this.unsubscribeNotifier)) + .subscribe(({ newDocumentType, documentTypes }) => { + this.documentTypes = documentTypes.results + this.documentTypeSelectionModel.toggle(newDocumentType.id) + }) + } + + createStoragePath(name: string) { + let modal = this.modalService.open(StoragePathEditDialogComponent, { + backdrop: 'static', + }) + modal.componentInstance.dialogMode = EditDialogMode.CREATE + modal.componentInstance.object = { name } + modal.componentInstance.succeeded + .pipe( + switchMap((newStoragePath) => { + return this.storagePathService + .listAll() + .pipe(map((storagePaths) => ({ newStoragePath, storagePaths }))) + }) + ) + .pipe(takeUntil(this.unsubscribeNotifier)) + .subscribe(({ newStoragePath, storagePaths }) => { + this.storagePaths = storagePaths.results + this.storagePathsSelectionModel.toggle(newStoragePath.id) + }) + } + applyDelete() { let modal = this.modalService.open(ConfirmDialogComponent, { backdrop: 'static', From 718171a1258628558ef6110fada9cac6e79e8d92 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Tue, 6 Feb 2024 14:50:08 -0800 Subject: [PATCH 30/41] Resolve svg rem width/height attribute warning --- .../document-card-small.component.html | 14 +++++++------- .../document-list/document-list.component.html | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html index cdbd88825..ea9ba9914 100644 --- a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html +++ b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html @@ -25,7 +25,7 @@ @if (notesEnabled && document.notes.length) { - + {{document.notes.length}} } @@ -43,14 +43,14 @@ @if (document.document_type) { } @if (document.storage_path) { } @@ -63,25 +63,25 @@
- + {{document.created_date | customDate:'mediumDate'}}
@if (document.archive_serial_number | isNumber) {
- + #{{document.archive_serial_number}}
} @if (document.owner && document.owner !== settingsService.currentUser.id) {
- + {{document.owner | username}}
} @if (document.is_shared_by_requester) {
- + Shared
} diff --git a/src-ui/src/app/components/document-list/document-list.component.html b/src-ui/src/app/components/document-list/document-list.component.html index 5de7ff6e7..5409306c6 100644 --- a/src-ui/src/app/components/document-list/document-list.component.html +++ b/src-ui/src/app/components/document-list/document-list.component.html @@ -232,7 +232,7 @@ @if (d.notes.length) { - + {{d.notes.length}} } From e98da2e72cc32a31a4db25a503504d2441d49bc9 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 7 Feb 2024 07:12:24 -0800 Subject: [PATCH 31/41] Fix whitespace in some frontend html files --- .../users-groups/users-groups.component.html | 112 +++++++-------- .../custom-fields.component.html | 24 ++-- .../manage/mail/mail.component.html | 130 +++++++++--------- .../manage/workflows/workflows.component.html | 24 ++-- 4 files changed, 145 insertions(+), 145 deletions(-) diff --git a/src-ui/src/app/components/admin/users-groups/users-groups.component.html b/src-ui/src/app/components/admin/users-groups/users-groups.component.html index 3f91842d4..a485a97ef 100644 --- a/src-ui/src/app/components/admin/users-groups/users-groups.component.html +++ b/src-ui/src/app/components/admin/users-groups/users-groups.component.html @@ -33,64 +33,64 @@
+ +
+
+
+ + } + +} + +@if (groups) { +

+ Groups + +

+ @if (groups.length > 0) { +
    +
  • +
    +
    Name
    +
    +
    +
    Actions
    +
    +
  • + @for (group of groups; track group) { +
  • +
    +
    +
    +
    +
    +
    + - -
    +
    -
  • - } -
- } - - @if (groups) { -

- Groups - -

- @if (groups.length > 0) { -
    -
  • -
    -
    Name
    -
    -
    -
    Actions
    -
    -
  • - @for (group of groups; track group) { -
  • -
    -
    -
    -
    -
    -
    - - -
    -
    -
    -
  • - } - @if (groups.length === 0) { -
  • No groups defined
  • - } -
- } - } - - @if (!users || !groups) { -
-
-
Loading...
- } + + } + @if (groups.length === 0) { +
  • No groups defined
  • + } + + } +} + +@if (!users || !groups) { +
    +
    +
    Loading...
    +
    +} diff --git a/src-ui/src/app/components/manage/custom-fields/custom-fields.component.html b/src-ui/src/app/components/manage/custom-fields/custom-fields.component.html index a803aae9c..2d4c77f93 100644 --- a/src-ui/src/app/components/manage/custom-fields/custom-fields.component.html +++ b/src-ui/src/app/components/manage/custom-fields/custom-fields.component.html @@ -29,16 +29,16 @@
    - -
    -
    + + - - } - @if (fields.length === 0) { -
  • No fields defined.
  • - } - + + + + } + @if (fields.length === 0) { +
  • No fields defined.
  • + } + diff --git a/src-ui/src/app/components/manage/mail/mail.component.html b/src-ui/src/app/components/manage/mail/mail.component.html index 68187c6bd..ef294fe43 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.html +++ b/src-ui/src/app/components/manage/mail/mail.component.html @@ -32,72 +32,72 @@
    - - -
    - - - - } - @if (mailAccounts.length === 0) { -
  • No mail accounts defined.
  • - } - - - - - -

    - Mail rules - -

    -
      -
    • -
      -
      Name
      -
      Account
      -
      Actions
      + + +
      -
    • + + + + } + @if (mailAccounts.length === 0) { +
    • No mail accounts defined.
    • + } +
    - @for (rule of mailRules; track rule) { -
  • -
    -
    -
    {{(mailAccountService.getCached(rule.account) | async)?.name}}
    -
    -
    - - - -
    -
    -
    -
  • - } - @if (mailRules.length === 0) { -
  • No mail rules defined.
  • - } - +
    - + +

    + Mail rules + +

    +
      +
    • +
      +
      Name
      +
      Account
      +
      Actions
      +
      +
    • - @if (!mailAccounts || !mailRules) { -
      -
      -
      Loading...
      -
      - } + @for (rule of mailRules; track rule) { +
    • +
      +
      +
      {{(mailAccountService.getCached(rule.account) | async)?.name}}
      +
      +
      + + + +
      +
      +
      +
    • + } + @if (mailRules.length === 0) { +
    • No mail rules defined.
    • + } +
    + +
    + +@if (!mailAccounts || !mailRules) { +
    +
    +
    Loading...
    +
    +} diff --git a/src-ui/src/app/components/manage/workflows/workflows.component.html b/src-ui/src/app/components/manage/workflows/workflows.component.html index 9dc214af4..9bc68fb49 100644 --- a/src-ui/src/app/components/manage/workflows/workflows.component.html +++ b/src-ui/src/app/components/manage/workflows/workflows.component.html @@ -33,16 +33,16 @@
    - -
    - + + - - } - @if (workflows.length === 0) { -
  • No workflows defined.
  • - } - + + + + } + @if (workflows.length === 0) { +
  • No workflows defined.
  • + } + From b47f30183114a73ef744aef1012d70c2cd56971e Mon Sep 17 00:00:00 2001 From: Trenton H <797416+stumpylog@users.noreply.github.com> Date: Wed, 7 Feb 2024 18:45:59 -0800 Subject: [PATCH 32/41] Adds the Python environment flags for no byte code saving, reducing the image size slightly (#5677) --- Dockerfile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 73bd0cf57..ea5bd6026 100644 --- a/Dockerfile +++ b/Dockerfile @@ -39,8 +39,6 @@ RUN set -eux \ # - Don't leave anything extra in here FROM docker.io/python:3.11-slim-bookworm as main-app -ENV PYTHONWARNINGS="ignore:::django.http.response:517" - LABEL org.opencontainers.image.authors="paperless-ngx team " LABEL org.opencontainers.image.documentation="https://docs.paperless-ngx.com/" LABEL org.opencontainers.image.source="https://github.com/paperless-ngx/paperless-ngx" @@ -57,6 +55,12 @@ ARG JBIG2ENC_VERSION=0.29 ARG QPDF_VERSION=11.6.4 ARG GS_VERSION=10.02.1 +# Set Python environment variables +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + # Ignore warning from Whitenoise + PYTHONWARNINGS="ignore:::django.http.response:517" + # # Begin installation and configuration # Order the steps below from least often changed to most From c508be6ecd417863b87662fa664952e97487e41c Mon Sep 17 00:00:00 2001 From: Moritz Pflanzer Date: Thu, 8 Feb 2024 17:15:38 +0100 Subject: [PATCH 33/41] Feature: OIDC & social authentication (#5190) --------- Co-authored-by: Moritz Pflanzer Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com> --- Pipfile | 1 + Pipfile.lock | 8 + docs/advanced_usage.md | 39 +++ docs/configuration.md | 44 +++ src-ui/messages.xlf | 73 +++- .../app-frame/app-frame.component.spec.ts | 22 ++ .../app-frame/app-frame.component.ts | 21 +- .../profile-edit-dialog.component.html | 37 ++ .../profile-edit-dialog.component.spec.ts | 73 ++++ .../profile-edit-dialog.component.ts | 31 ++ src-ui/src/app/data/user-profile.ts | 13 + .../services/django-messages.service.spec.ts | 30 ++ .../app/services/django-messages.service.ts | 27 ++ .../src/app/services/profile.service.spec.ts | 16 + src-ui/src/app/services/profile.service.ts | 18 +- .../{registration => account}/login.html | 36 +- .../password_reset.html} | 6 +- .../password_reset_done.html | 0 .../password_reset_from_key.html} | 14 +- .../password_reset_from_key_done.html} | 2 +- src/documents/templates/index.html | 7 + .../authentication_error.html} | 14 +- .../templates/socialaccount/login.html | 52 +++ .../templates/socialaccount/signup.html | 77 +++++ src/documents/tests/test_api_profile.py | 198 ++++++++++- .../tests/test_management_exporter.py | 18 +- src/locale/en_US/LC_MESSAGES/django.po | 322 ++++++++++-------- src/paperless/adapter.py | 30 ++ src/paperless/serialisers.py | 23 ++ src/paperless/settings.py | 25 ++ src/paperless/tests/test_adapter.py | 43 +++ src/paperless/urls.py | 12 +- src/paperless/views.py | 55 +++ 33 files changed, 1197 insertions(+), 190 deletions(-) create mode 100644 src-ui/src/app/services/django-messages.service.spec.ts create mode 100644 src-ui/src/app/services/django-messages.service.ts rename src/documents/templates/{registration => account}/login.html (83%) rename src/documents/templates/{registration/password_reset_form.html => account/password_reset.html} (96%) rename src/documents/templates/{registration => account}/password_reset_done.html (100%) rename src/documents/templates/{registration/password_reset_confirm.html => account/password_reset_from_key.html} (92%) rename src/documents/templates/{registration/password_reset_complete.html => account/password_reset_from_key_done.html} (99%) rename src/documents/templates/{registration/logged_out.html => socialaccount/authentication_error.html} (93%) create mode 100644 src/documents/templates/socialaccount/login.html create mode 100644 src/documents/templates/socialaccount/signup.html create mode 100644 src/paperless/adapter.py create mode 100644 src/paperless/tests/test_adapter.py diff --git a/Pipfile b/Pipfile index afc294412..6d5c7f861 100644 --- a/Pipfile +++ b/Pipfile @@ -8,6 +8,7 @@ dateparser = "~=1.2" # WARNING: django does not use semver. # Only patch versions are guaranteed to not introduce breaking changes. django = "~=4.2.9" +django-allauth = "*" django-auditlog = "*" django-celery-results = "*" django-compression-middleware = "*" diff --git a/Pipfile.lock b/Pipfile.lock index ceef1c8a9..d177517c2 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -452,6 +452,14 @@ "markers": "python_version >= '3.8'", "version": "==4.2.9" }, + "django-allauth": { + "hashes": [ + "sha256:ec19efb80b34d2f18bd831eab9b10b6301f58d1cce9f39af35f497b7e5b0a141" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==0.59.0" + }, "django-auditlog": { "hashes": [ "sha256:7bc2c87e4aff62dec9785d1b2359a2b27148f8c286f8a52b9114fc7876c5a9f7", diff --git a/docs/advanced_usage.md b/docs/advanced_usage.md index 04626fe41..46d9c2b4b 100644 --- a/docs/advanced_usage.md +++ b/docs/advanced_usage.md @@ -640,3 +640,42 @@ single-sided split marker page, the split document(s) will have an empty page at whatever else was on the backside of the split marker page.) You can work around that by having a split marker page that has the split barcode on _both_ sides. This way, the extra page will get automatically removed. + +## SSO and third party authentication with Paperless-ngx + +Paperless-ngx has a built-in authentication system from Django but you can easily integrate an +external authentication solution using one of the following methods: + +### Remote User authentication + +This is a simple option that uses remote user authentication made available by certain SSO +applications. See the relevant configuration options for more information: +[PAPERLESS_ENABLE_HTTP_REMOTE_USER](configuration.md#PAPERLESS_ENABLE_HTTP_REMOTE_USER) and +[PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME](configuration.md#PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME) + +### OpenID Connect and social authentication + +Version 2.5.0 of Paperless-ngx added support for integrating other authentication systems via +the [django-allauth](https://github.com/pennersr/django-allauth) package. Once set up, users +can either log in or (optionally) sign up using any third party systems you integrate. See the +relevant [configuration settings](configuration.md#PAPERLESS_SOCIALACCOUNT_PROVIDERS) and +[django-allauth docs](https://docs.allauth.org/en/latest/socialaccount/configuration.html) +for more information. + +As an example, to set up login via Github, the following environment variables would need to be +set: + +```conf +PAPERLESS_APPS="allauth.socialaccount.providers.github" +PAPERLESS_SOCIALACCOUNT_PROVIDERS='{"github": {"APPS": [{"provider_id": "github","name": "Github","client_id": "","secret": ""}]}}' +``` + +Or, to use OpenID Connect ("OIDC"), via Keycloak in this example: + +```conf +PAPERLESS_APPS="allauth.socialaccount.providers.openid_connect" +PAPERLESS_SOCIALACCOUNT_PROVIDERS=' +{"openid_connect": {"APPS": [{"provider_id": "keycloak","name": "Keycloak","client_id": "paperless","secret": "","settings": { "server_url": "https:///realms//.well-known/openid-configuration"}}]}}' +``` + +More details about configuration option for various providers can be found in the allauth documentation: https://docs.allauth.org/en/latest/socialaccount/providers/index.html#provider-specifics diff --git a/docs/configuration.md b/docs/configuration.md index e99e0a085..3d1b1d1d1 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -535,6 +535,42 @@ This is for use with self-signed certificates against local IMAP servers. Settings this value has security implications for the security of your email. Understand what it does and be sure you need to before setting. +#### [`PAPERLESS_SOCIALACCOUNT_PROVIDERS=`](#PAPERLESS_SOCIALACCOUNT_PROVIDERS) {#PAPERLESS_SOCIALACCOUNT_PROVIDERS} + +: This variable is used to setup login and signup via social account providers which are compatible with django-allauth. +See the corresponding [django-allauth documentation](https://docs.allauth.org/en/0.60.0/socialaccount/providers/index.html) +for a list of provider configurations. You will also likely need to include the relevant Django 'application' inside the +[PAPERLESS_APPS](#PAPERLESS_APPS) setting. + + Defaults to None, which does not enable any third party authentication systems. + +#### [`PAPERLESS_SOCIAL_AUTO_SIGNUP=`](#PAPERLESS_SOCIAL_AUTO_SIGNUP) {#PAPERLESS_SOCIAL_AUTO_SIGNUP} + +: Attempt to signup the user using retrieved email, username etc from the third party authentication +system. See the corresponding +[django-allauth documentation](https://docs.allauth.org/en/0.60.0/socialaccount/configuration.html) + + Defaults to False + +#### [`PAPERLESS_SOCIALACCOUNT_ALLOW_SIGNUPS=`](#PAPERLESS_SOCIALACCOUNT_ALLOW_SIGNUPS) {#PAPERLESS_SOCIALACCOUNT_ALLOW_SIGNUPS} + +: Allow users to signup for a new Paperless-ngx account using any setup third party authentication systems. + + Defaults to True + +#### [`PAPERLESS_ACCOUNT_ALLOW_SIGNUPS=`](#PAPERLESS_ACCOUNT_ALLOW_SIGNUPS) {#PAPERLESS_ACCOUNT_ALLOW_SIGNUPS} + +: Allow users to signup for a new Paperless-ngx account. + + Defaults to False + +#### [`PAPERLESS_ACCOUNT_DEFAULT_HTTP_PROTOCOL=`](#PAPERLESS_ACCOUNT_DEFAULT_HTTP_PROTOCOL) {#PAPERLESS_ACCOUNT_DEFAULT_HTTP_PROTOCOL} + +: The protocol used when generating URLs, e.g. login callback URLs. See the corresponding +[django-allauth documentation](https://docs.allauth.org/en/latest/account/configuration.html) + + Defaults to 'https' + ## OCR settings {#ocr} Paperless uses [OCRmyPDF](https://ocrmypdf.readthedocs.io/en/latest/) @@ -905,6 +941,14 @@ documents. Default is none, which disables the temporary directory. +#### [`PAPERLESS_APPS=`](#PAPERLESS_APPS) {#PAPERLESS_APPS} + +: A comma-separated list of Django apps to be included in Django's +[`INSTALLED_APPS`](https://docs.djangoproject.com/en/5.0/ref/applications/). This setting should +be used with caution! + + Defaults to None, which does not add any additional apps. + ## Document Consumption {#consume_config} #### [`PAPERLESS_CONSUMER_DELETE_DUPLICATES=`](#PAPERLESS_CONSUMER_DELETE_DUPLICATES) {#PAPERLESS_CONSUMER_DELETE_DUPLICATES} diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 9f163e3b8..f2b356d9a 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -502,7 +502,7 @@ src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 55 + 92 src/app/components/document-detail/document-detail.component.html @@ -1563,7 +1563,7 @@ src/app/components/app-frame/app-frame.component.ts - 121 + 140 @@ -1938,7 +1938,7 @@ src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 145 + 159 @@ -2405,21 +2405,21 @@ Sidebar views updated src/app/components/app-frame/app-frame.component.ts - 263 + 282 Error updating sidebar views src/app/components/app-frame/app-frame.component.ts - 266 + 285 An error occurred while saving update checking settings. src/app/components/app-frame/app-frame.component.ts - 287 + 306 @@ -2523,7 +2523,7 @@ src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 54 + 91 src/app/components/common/select-dialog/select-dialog.component.html @@ -4103,39 +4103,88 @@ 50 + + Connected social accounts + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 54 + + + + Set a password before disconnecting social account. + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 58 + + + + Disconnect social account + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 68 + + + + Disconnect + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 69 + + + + Warning: disconnecting social accounts cannot be undone + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 74 + + + + Connect new social account + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 79 + + Emails must match src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 94 + 108 Passwords must match src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 122 + 136 Profile updated successfully src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 142 + 156 Error saving profile src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 154 + 168 Error generating auth token src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 171 + 185 + + + + Error disconnecting social account + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts + 210 diff --git a/src-ui/src/app/components/app-frame/app-frame.component.spec.ts b/src-ui/src/app/components/app-frame/app-frame.component.spec.ts index 64877bb09..e1a553047 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.spec.ts +++ b/src-ui/src/app/components/app-frame/app-frame.component.spec.ts @@ -21,6 +21,10 @@ import { IfPermissionsDirective } from 'src/app/directives/if-permissions.direct import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { of, throwError } from 'rxjs' import { ToastService } from 'src/app/services/toast.service' +import { + DjangoMessageLevel, + DjangoMessagesService, +} from 'src/app/services/django-messages.service' import { environment } from 'src/environments/environment' import { OpenDocumentsService } from 'src/app/services/open-documents.service' import { ActivatedRoute, Router } from '@angular/router' @@ -83,6 +87,7 @@ describe('AppFrameComponent', () => { let permissionsService: PermissionsService let remoteVersionService: RemoteVersionService let toastService: ToastService + let messagesService: DjangoMessagesService let openDocumentsService: OpenDocumentsService let searchService: SearchService let documentListViewService: DocumentListViewService @@ -123,6 +128,7 @@ describe('AppFrameComponent', () => { RemoteVersionService, IfPermissionsDirective, ToastService, + DjangoMessagesService, OpenDocumentsService, SearchService, NgbModal, @@ -151,6 +157,7 @@ describe('AppFrameComponent', () => { permissionsService = TestBed.inject(PermissionsService) remoteVersionService = TestBed.inject(RemoteVersionService) toastService = TestBed.inject(ToastService) + messagesService = TestBed.inject(DjangoMessagesService) openDocumentsService = TestBed.inject(OpenDocumentsService) searchService = TestBed.inject(SearchService) documentListViewService = TestBed.inject(DocumentListViewService) @@ -393,4 +400,19 @@ describe('AppFrameComponent', () => { backdrop: 'static', }) }) + + it('should show toasts for django messages', () => { + const toastErrorSpy = jest.spyOn(toastService, 'showError') + const toastInfoSpy = jest.spyOn(toastService, 'showInfo') + jest.spyOn(messagesService, 'get').mockReturnValue([ + { level: DjangoMessageLevel.WARNING, message: 'Test warning' }, + { level: DjangoMessageLevel.ERROR, message: 'Test error' }, + { level: DjangoMessageLevel.SUCCESS, message: 'Test success' }, + { level: DjangoMessageLevel.INFO, message: 'Test info' }, + { level: DjangoMessageLevel.DEBUG, message: 'Test debug' }, + ]) + component.ngOnInit() + expect(toastErrorSpy).toHaveBeenCalledTimes(2) + expect(toastInfoSpy).toHaveBeenCalledTimes(3) + }) }) diff --git a/src-ui/src/app/components/app-frame/app-frame.component.ts b/src-ui/src/app/components/app-frame/app-frame.component.ts index cfc9740a4..ab9322380 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.ts +++ b/src-ui/src/app/components/app-frame/app-frame.component.ts @@ -12,6 +12,10 @@ import { } from 'rxjs/operators' import { Document } from 'src/app/data/document' import { OpenDocumentsService } from 'src/app/services/open-documents.service' +import { + DjangoMessageLevel, + DjangoMessagesService, +} from 'src/app/services/django-messages.service' import { SavedViewService } from 'src/app/services/rest/saved-view.service' import { SearchService } from 'src/app/services/rest/search.service' import { environment } from 'src/environments/environment' @@ -73,7 +77,8 @@ export class AppFrameComponent public tasksService: TasksService, private readonly toastService: ToastService, private modalService: NgbModal, - permissionsService: PermissionsService + public permissionsService: PermissionsService, + private djangoMessagesService: DjangoMessagesService ) { super() @@ -92,6 +97,20 @@ export class AppFrameComponent this.checkForUpdates() } this.tasksService.reload() + + this.djangoMessagesService.get().forEach((message) => { + switch (message.level) { + case DjangoMessageLevel.ERROR: + case DjangoMessageLevel.WARNING: + this.toastService.showError(message.message) + break + case DjangoMessageLevel.SUCCESS: + case DjangoMessageLevel.INFO: + case DjangoMessageLevel.DEBUG: + this.toastService.showInfo(message.message) + break + } + }) } toggleSlimSidebar(): void { diff --git a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html index 394ba4449..6b06dfa8e 100644 --- a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html +++ b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html @@ -49,6 +49,43 @@
    Warning: changing the token cannot be undone
    + @if (socialAccounts?.length > 0) { +
    +

    Connected social accounts

    +
      + @for (account of socialAccounts; track account.id) { +
    • + {{account.name}} ({{account.provider}}) + +
    • + } +
    +
    Warning: disconnecting social accounts cannot be undone
    +
    + } + @if (socialAccountProviders?.length > 0) { +
    +

    Connect new social account

    +
    + @for (provider of socialAccountProviders; track provider.name) { + + {{provider.name}}  + + } +
    +
    + } diff --git a/src/documents/templates/socialaccount/login.html b/src/documents/templates/socialaccount/login.html new file mode 100644 index 000000000..f135a77fe --- /dev/null +++ b/src/documents/templates/socialaccount/login.html @@ -0,0 +1,52 @@ + + +{% load static %} +{% load i18n %} +{% load allauth %} + + + + + + + + + + {% translate "Paperless-ngx social account sign in" %} + + + + + + +
    + +
    + + diff --git a/src/documents/templates/socialaccount/signup.html b/src/documents/templates/socialaccount/signup.html new file mode 100644 index 000000000..ef208d8ad --- /dev/null +++ b/src/documents/templates/socialaccount/signup.html @@ -0,0 +1,77 @@ + + +{% load static %} +{% load i18n %} + + + + + + + + + + {% translate "Paperless-ngx social account sign up" %} + + + + + + +
    + +
    + + diff --git a/src/documents/tests/test_api_profile.py b/src/documents/tests/test_api_profile.py index 9e12b1ed3..eede0d2b0 100644 --- a/src/documents/tests/test_api_profile.py +++ b/src/documents/tests/test_api_profile.py @@ -1,3 +1,7 @@ +from unittest import mock + +from allauth.socialaccount.models import SocialAccount +from allauth.socialaccount.models import SocialApp from django.contrib.auth.models import User from rest_framework import status from rest_framework.authtoken.models import Token @@ -6,6 +10,44 @@ from rest_framework.test import APITestCase from documents.tests.utils import DirectoriesMixin +# see allauth.socialaccount.providers.openid.provider.OpenIDProvider +class MockOpenIDProvider: + id = "openid" + name = "OpenID" + + def get_brands(self): + default_servers = [ + dict(id="yahoo", name="Yahoo", openid_url="http://me.yahoo.com"), + dict(id="hyves", name="Hyves", openid_url="http://hyves.nl"), + ] + return default_servers + + def get_login_url(self, request, **kwargs): + return "openid/login/" + + +# see allauth.socialaccount.providers.openid_connect.provider.OpenIDConnectProviderAccount +class MockOpenIDConnectProviderAccount: + def __init__(self, mock_social_account_dict): + self.account = mock_social_account_dict + + def to_str(self): + return self.account["name"] + + +# see allauth.socialaccount.providers.openid_connect.provider.OpenIDConnectProvider +class MockOpenIDConnectProvider: + id = "openid_connect" + name = "OpenID Connect" + + def __init__(self, app=None): + self.app = app + self.name = app.name + + def get_login_url(self, request, **kwargs): + return f"{self.app.provider_id}/login/?process=connect" + + class TestApiProfile(DirectoriesMixin, APITestCase): ENDPOINT = "/api/profile/" @@ -19,6 +61,17 @@ class TestApiProfile(DirectoriesMixin, APITestCase): ) self.client.force_authenticate(user=self.user) + def setupSocialAccount(self): + SocialApp.objects.create( + name="Keycloak", + provider="openid_connect", + provider_id="keycloak-test", + ) + self.user.socialaccount_set.add( + SocialAccount(uid="123456789", provider="keycloak-test"), + bulk=False, + ) + def test_get_profile(self): """ GIVEN: @@ -28,7 +81,6 @@ class TestApiProfile(DirectoriesMixin, APITestCase): THEN: - Profile is returned """ - response = self.client.get(self.ENDPOINT) self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -37,6 +89,52 @@ class TestApiProfile(DirectoriesMixin, APITestCase): self.assertEqual(response.data["first_name"], self.user.first_name) self.assertEqual(response.data["last_name"], self.user.last_name) + @mock.patch( + "allauth.socialaccount.models.SocialAccount.get_provider_account", + ) + @mock.patch( + "allauth.socialaccount.adapter.DefaultSocialAccountAdapter.list_providers", + ) + def test_get_profile_w_social(self, mock_list_providers, mock_get_provider_account): + """ + GIVEN: + - Configured user and setup social account + WHEN: + - API call is made to get profile + THEN: + - Profile is returned with social accounts + """ + self.setupSocialAccount() + + openid_provider = ( + MockOpenIDConnectProvider( + app=SocialApp.objects.get(provider_id="keycloak-test"), + ), + ) + mock_list_providers.return_value = [ + openid_provider, + ] + mock_get_provider_account.return_value = MockOpenIDConnectProviderAccount( + mock_social_account_dict={ + "name": openid_provider[0].name, + }, + ) + + response = self.client.get(self.ENDPOINT) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + self.assertEqual( + response.data["social_accounts"], + [ + { + "id": 1, + "provider": "keycloak-test", + "name": "Keycloak", + }, + ], + ) + def test_update_profile(self): """ GIVEN: @@ -103,3 +201,101 @@ class TestApiProfile(DirectoriesMixin, APITestCase): response = self.client.post(f"{self.ENDPOINT}generate_auth_token/") self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + @mock.patch( + "allauth.socialaccount.adapter.DefaultSocialAccountAdapter.list_providers", + ) + def test_get_social_account_providers( + self, + mock_list_providers, + ): + """ + GIVEN: + - Configured user + WHEN: + - API call is made to get social account providers + THEN: + - Social account providers are returned + """ + self.setupSocialAccount() + + mock_list_providers.return_value = [ + MockOpenIDConnectProvider( + app=SocialApp.objects.get(provider_id="keycloak-test"), + ), + ] + + response = self.client.get(f"{self.ENDPOINT}social_account_providers/") + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual( + response.data[0]["name"], + "Keycloak", + ) + self.assertIn( + "keycloak-test/login/?process=connect", + response.data[0]["login_url"], + ) + + @mock.patch( + "allauth.socialaccount.adapter.DefaultSocialAccountAdapter.list_providers", + ) + def test_get_social_account_providers_openid( + self, + mock_list_providers, + ): + """ + GIVEN: + - Configured user and openid social account provider + WHEN: + - API call is made to get social account providers + THEN: + - Brands for openid provider are returned + """ + + mock_list_providers.return_value = [ + MockOpenIDProvider(), + ] + + response = self.client.get(f"{self.ENDPOINT}social_account_providers/") + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual( + len(response.data), + 2, + ) + + def test_disconnect_social_account(self): + """ + GIVEN: + - Configured user + WHEN: + - API call is made to disconnect a social account + THEN: + - Social account is deleted from the user or request fails + """ + self.setupSocialAccount() + + # Test with invalid id + response = self.client.post( + f"{self.ENDPOINT}disconnect_social_account/", + {"id": -1}, + ) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + # Test with valid id + social_account_id = self.user.socialaccount_set.all()[0].pk + + response = self.client.post( + f"{self.ENDPOINT}disconnect_social_account/", + {"id": social_account_id}, + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, social_account_id) + + self.assertEqual( + len(self.user.socialaccount_set.filter(pk=social_account_id)), + 0, + ) diff --git a/src/documents/tests/test_management_exporter.py b/src/documents/tests/test_management_exporter.py index 888572b58..226b89694 100644 --- a/src/documents/tests/test_management_exporter.py +++ b/src/documents/tests/test_management_exporter.py @@ -177,9 +177,9 @@ class TestExportImport(DirectoriesMixin, FileSystemAssertsMixin, TestCase): os.path.join(self.dirs.media_dir, "documents"), ) - manifest = self._do_export(use_filename_format=use_filename_format) + num_permission_objects = Permission.objects.count() - self.assertEqual(len(manifest), 190) + manifest = self._do_export(use_filename_format=use_filename_format) # dont include consumer or AnonymousUser users self.assertEqual( @@ -273,7 +273,7 @@ class TestExportImport(DirectoriesMixin, FileSystemAssertsMixin, TestCase): self.assertEqual(Document.objects.get(id=self.d4.id).title, "wow_dec") self.assertEqual(GroupObjectPermission.objects.count(), 1) self.assertEqual(UserObjectPermission.objects.count(), 1) - self.assertEqual(Permission.objects.count(), 136) + self.assertEqual(Permission.objects.count(), num_permission_objects) messages = check_sanity() # everything is alright after the test self.assertEqual(len(messages), 0) @@ -753,15 +753,15 @@ class TestExportImport(DirectoriesMixin, FileSystemAssertsMixin, TestCase): os.path.join(self.dirs.media_dir, "documents"), ) - self.assertEqual(ContentType.objects.count(), 34) - self.assertEqual(Permission.objects.count(), 136) + num_content_type_objects = ContentType.objects.count() + num_permission_objects = Permission.objects.count() manifest = self._do_export() with paperless_environment(): self.assertEqual( len(list(filter(lambda e: e["model"] == "auth.permission", manifest))), - 136, + num_permission_objects, ) # add 1 more to db to show objects are not re-created by import Permission.objects.create( @@ -769,7 +769,7 @@ class TestExportImport(DirectoriesMixin, FileSystemAssertsMixin, TestCase): codename="test_perm", content_type_id=1, ) - self.assertEqual(Permission.objects.count(), 137) + self.assertEqual(Permission.objects.count(), num_permission_objects + 1) # will cause an import error self.user.delete() @@ -778,5 +778,5 @@ class TestExportImport(DirectoriesMixin, FileSystemAssertsMixin, TestCase): with self.assertRaises(IntegrityError): call_command("document_importer", "--no-progress-bar", self.target) - self.assertEqual(ContentType.objects.count(), 34) - self.assertEqual(Permission.objects.count(), 137) + self.assertEqual(ContentType.objects.count(), num_content_type_objects) + self.assertEqual(Permission.objects.count(), num_permission_objects + 1) diff --git a/src/locale/en_US/LC_MESSAGES/django.po b/src/locale/en_US/LC_MESSAGES/django.po index 0c7242462..7ee0c0d8b 100644 --- a/src/locale/en_US/LC_MESSAGES/django.po +++ b/src/locale/en_US/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: paperless-ngx\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-02-02 20:17-0800\n" +"POT-Creation-Date: 2024-02-07 06:20+0000\n" "PO-Revision-Date: 2022-02-17 04:17\n" "Last-Translator: \n" "Language-Team: English\n" @@ -777,15 +777,136 @@ msgstr "" msgid "Invalid color." msgstr "" -#: documents/serialisers.py:1049 +#: documents/serialisers.py:1060 #, python-format msgid "File type %(type)s not supported" msgstr "" -#: documents/serialisers.py:1152 +#: documents/serialisers.py:1163 msgid "Invalid variable detected." msgstr "" +#: documents/templates/account/login.html:14 +msgid "Paperless-ngx sign in" +msgstr "" + +#: documents/templates/account/login.html:47 +msgid "Please sign in." +msgstr "" + +#: documents/templates/account/login.html:50 +msgid "Your username and password didn't match. Please try again." +msgstr "" + +#: documents/templates/account/login.html:54 +msgid "Share link was not found." +msgstr "" + +#: documents/templates/account/login.html:58 +msgid "Share link has expired." +msgstr "" + +#: documents/templates/account/login.html:61 +#: documents/templates/socialaccount/signup.html:56 +msgid "Username" +msgstr "" + +#: documents/templates/account/login.html:62 +msgid "Password" +msgstr "" + +#: documents/templates/account/login.html:72 +msgid "Sign in" +msgstr "" + +#: documents/templates/account/login.html:76 +msgid "Forgot your password?" +msgstr "" + +#: documents/templates/account/login.html:83 +msgid "or sign in via" +msgstr "" + +#: documents/templates/account/password_reset.html:15 +msgid "Paperless-ngx reset password request" +msgstr "" + +#: documents/templates/account/password_reset.html:43 +msgid "" +"Enter your email address below, and we'll email instructions for setting a " +"new one." +msgstr "" + +#: documents/templates/account/password_reset.html:46 +msgid "An error occurred. Please try again." +msgstr "" + +#: documents/templates/account/password_reset.html:49 +#: documents/templates/socialaccount/signup.html:57 +msgid "Email" +msgstr "" + +#: documents/templates/account/password_reset.html:56 +msgid "Send me instructions!" +msgstr "" + +#: documents/templates/account/password_reset_done.html:14 +msgid "Paperless-ngx reset password sent" +msgstr "" + +#: documents/templates/account/password_reset_done.html:40 +msgid "Check your inbox." +msgstr "" + +#: documents/templates/account/password_reset_done.html:41 +msgid "" +"We've emailed you instructions for setting your password. You should receive " +"the email shortly!" +msgstr "" + +#: documents/templates/account/password_reset_from_key.html:15 +msgid "Paperless-ngx reset password confirmation" +msgstr "" + +#: documents/templates/account/password_reset_from_key.html:44 +msgid "request a new password reset" +msgstr "" + +#: documents/templates/account/password_reset_from_key.html:46 +msgid "Set a new password." +msgstr "" + +#: documents/templates/account/password_reset_from_key.html:50 +msgid "Passwords did not match or too weak. Try again." +msgstr "" + +#: documents/templates/account/password_reset_from_key.html:53 +msgid "New Password" +msgstr "" + +#: documents/templates/account/password_reset_from_key.html:54 +msgid "Confirm Password" +msgstr "" + +#: documents/templates/account/password_reset_from_key.html:65 +msgid "Change my password" +msgstr "" + +#: documents/templates/account/password_reset_from_key_done.html:14 +msgid "Paperless-ngx reset password complete" +msgstr "" + +#: documents/templates/account/password_reset_from_key_done.html:40 +msgid "Password reset complete." +msgstr "" + +#: documents/templates/account/password_reset_from_key_done.html:42 +#, python-format +msgid "" +"Your new password has been set. You can now log " +"in" +msgstr "" + #: documents/templates/index.html:79 msgid "Paperless-ngx is loading..." msgstr "" @@ -798,131 +919,40 @@ msgstr "" msgid "Here's a link to the docs." msgstr "" -#: documents/templates/registration/logged_out.html:14 -msgid "Paperless-ngx signed out" +#: documents/templates/socialaccount/authentication_error.html:15 +#: documents/templates/socialaccount/login.html:15 +msgid "Paperless-ngx social account sign in" msgstr "" -#: documents/templates/registration/logged_out.html:40 -msgid "You have been successfully logged out. Bye!" -msgstr "" - -#: documents/templates/registration/logged_out.html:41 -msgid "Sign in again" -msgstr "" - -#: documents/templates/registration/login.html:14 -msgid "Paperless-ngx sign in" -msgstr "" - -#: documents/templates/registration/login.html:41 -msgid "Please sign in." -msgstr "" - -#: documents/templates/registration/login.html:44 -msgid "Your username and password didn't match. Please try again." -msgstr "" - -#: documents/templates/registration/login.html:48 -msgid "Share link was not found." -msgstr "" - -#: documents/templates/registration/login.html:52 -msgid "Share link has expired." -msgstr "" - -#: documents/templates/registration/login.html:55 -msgid "Username" -msgstr "" - -#: documents/templates/registration/login.html:56 -msgid "Password" -msgstr "" - -#: documents/templates/registration/login.html:66 -msgid "Sign in" -msgstr "" - -#: documents/templates/registration/login.html:70 -msgid "Forgot your password?" -msgstr "" - -#: documents/templates/registration/password_reset_complete.html:14 -msgid "Paperless-ngx reset password complete" -msgstr "" - -#: documents/templates/registration/password_reset_complete.html:40 -msgid "Password reset complete." -msgstr "" - -#: documents/templates/registration/password_reset_complete.html:42 +#: documents/templates/socialaccount/authentication_error.html:43 #, python-format msgid "" -"Your new password has been set. You can now log " -"in" +"An error occurred while attempting to login via your social network account. " +"Back to the login page" msgstr "" -#: documents/templates/registration/password_reset_confirm.html:14 -msgid "Paperless-ngx reset password confirmation" +#: documents/templates/socialaccount/login.html:44 +#, python-format +msgid "You are about to connect a new third-party account from %(provider)s." msgstr "" -#: documents/templates/registration/password_reset_confirm.html:42 -msgid "Set a new password." +#: documents/templates/socialaccount/login.html:47 +msgid "Continue" msgstr "" -#: documents/templates/registration/password_reset_confirm.html:46 -msgid "Passwords did not match or too weak. Try again." +#: documents/templates/socialaccount/signup.html:14 +msgid "Paperless-ngx social account sign up" msgstr "" -#: documents/templates/registration/password_reset_confirm.html:49 -msgid "New Password" -msgstr "" - -#: documents/templates/registration/password_reset_confirm.html:50 -msgid "Confirm Password" -msgstr "" - -#: documents/templates/registration/password_reset_confirm.html:61 -msgid "Change my password" -msgstr "" - -#: documents/templates/registration/password_reset_confirm.html:65 -msgid "request a new password reset" -msgstr "" - -#: documents/templates/registration/password_reset_done.html:14 -msgid "Paperless-ngx reset password sent" -msgstr "" - -#: documents/templates/registration/password_reset_done.html:40 -msgid "Check your inbox." -msgstr "" - -#: documents/templates/registration/password_reset_done.html:41 +#: documents/templates/socialaccount/signup.html:53 +#, python-format msgid "" -"We've emailed you instructions for setting your password. You should receive " -"the email shortly!" +"You are about to use your %(provider_name)s account to login to\n" +"%(site_name)s. As a final step, please complete the following form:" msgstr "" -#: documents/templates/registration/password_reset_form.html:14 -msgid "Paperless-ngx reset password request" -msgstr "" - -#: documents/templates/registration/password_reset_form.html:41 -msgid "" -"Enter your email address below, and we'll email instructions for setting a " -"new one." -msgstr "" - -#: documents/templates/registration/password_reset_form.html:44 -msgid "An error occurred. Please try again." -msgstr "" - -#: documents/templates/registration/password_reset_form.html:47 -msgid "Email" -msgstr "" - -#: documents/templates/registration/password_reset_form.html:54 -msgid "Send me instructions!" +#: documents/templates/socialaccount/signup.html:72 +msgid "Sign up" msgstr "" #: documents/validators.py:17 @@ -1088,135 +1118,135 @@ msgstr "" msgid "paperless application settings" msgstr "" -#: paperless/settings.py:617 +#: paperless/settings.py:658 msgid "English (US)" msgstr "" -#: paperless/settings.py:618 +#: paperless/settings.py:659 msgid "Arabic" msgstr "" -#: paperless/settings.py:619 +#: paperless/settings.py:660 msgid "Afrikaans" msgstr "" -#: paperless/settings.py:620 +#: paperless/settings.py:661 msgid "Belarusian" msgstr "" -#: paperless/settings.py:621 +#: paperless/settings.py:662 msgid "Bulgarian" msgstr "" -#: paperless/settings.py:622 +#: paperless/settings.py:663 msgid "Catalan" msgstr "" -#: paperless/settings.py:623 +#: paperless/settings.py:664 msgid "Czech" msgstr "" -#: paperless/settings.py:624 +#: paperless/settings.py:665 msgid "Danish" msgstr "" -#: paperless/settings.py:625 +#: paperless/settings.py:666 msgid "German" msgstr "" -#: paperless/settings.py:626 +#: paperless/settings.py:667 msgid "Greek" msgstr "" -#: paperless/settings.py:627 +#: paperless/settings.py:668 msgid "English (GB)" msgstr "" -#: paperless/settings.py:628 +#: paperless/settings.py:669 msgid "Spanish" msgstr "" -#: paperless/settings.py:629 +#: paperless/settings.py:670 msgid "Finnish" msgstr "" -#: paperless/settings.py:630 +#: paperless/settings.py:671 msgid "French" msgstr "" -#: paperless/settings.py:631 +#: paperless/settings.py:672 msgid "Hungarian" msgstr "" -#: paperless/settings.py:632 +#: paperless/settings.py:673 msgid "Italian" msgstr "" -#: paperless/settings.py:633 +#: paperless/settings.py:674 msgid "Japanese" msgstr "" -#: paperless/settings.py:634 +#: paperless/settings.py:675 msgid "Luxembourgish" msgstr "" -#: paperless/settings.py:635 +#: paperless/settings.py:676 msgid "Norwegian" msgstr "" -#: paperless/settings.py:636 +#: paperless/settings.py:677 msgid "Dutch" msgstr "" -#: paperless/settings.py:637 +#: paperless/settings.py:678 msgid "Polish" msgstr "" -#: paperless/settings.py:638 +#: paperless/settings.py:679 msgid "Portuguese (Brazil)" msgstr "" -#: paperless/settings.py:639 +#: paperless/settings.py:680 msgid "Portuguese" msgstr "" -#: paperless/settings.py:640 +#: paperless/settings.py:681 msgid "Romanian" msgstr "" -#: paperless/settings.py:641 +#: paperless/settings.py:682 msgid "Russian" msgstr "" -#: paperless/settings.py:642 +#: paperless/settings.py:683 msgid "Slovak" msgstr "" -#: paperless/settings.py:643 +#: paperless/settings.py:684 msgid "Slovenian" msgstr "" -#: paperless/settings.py:644 +#: paperless/settings.py:685 msgid "Serbian" msgstr "" -#: paperless/settings.py:645 +#: paperless/settings.py:686 msgid "Swedish" msgstr "" -#: paperless/settings.py:646 +#: paperless/settings.py:687 msgid "Turkish" msgstr "" -#: paperless/settings.py:647 +#: paperless/settings.py:688 msgid "Ukrainian" msgstr "" -#: paperless/settings.py:648 +#: paperless/settings.py:689 msgid "Chinese Simplified" msgstr "" -#: paperless/urls.py:214 +#: paperless/urls.py:224 msgid "Paperless-ngx administration" msgstr "" diff --git a/src/paperless/adapter.py b/src/paperless/adapter.py new file mode 100644 index 000000000..98b0f11ba --- /dev/null +++ b/src/paperless/adapter.py @@ -0,0 +1,30 @@ +from allauth.account.adapter import DefaultAccountAdapter +from allauth.socialaccount.adapter import DefaultSocialAccountAdapter +from django.conf import settings +from django.urls import reverse + + +class CustomAccountAdapter(DefaultAccountAdapter): + def is_open_for_signup(self, request): + allow_signups = super().is_open_for_signup(request) + # Override with setting, otherwise default to super. + return getattr(settings, "ACCOUNT_ALLOW_SIGNUPS", allow_signups) + + +class CustomSocialAccountAdapter(DefaultSocialAccountAdapter): + def is_open_for_signup(self, request, sociallogin): + allow_signups = super().is_open_for_signup(request, sociallogin) + # Override with setting, otherwise default to super. + return getattr(settings, "SOCIALACCOUNT_ALLOW_SIGNUPS", allow_signups) + + def get_connect_redirect_url(self, request, socialaccount): + """ + Returns the default URL to redirect to after successfully + connecting a social account. + """ + url = reverse("base") + return url + + def populate_user(self, request, sociallogin, data): + # TODO: If default global permissions are implemented, should also be here + return super().populate_user(request, sociallogin, data) # pragma: no cover diff --git a/src/paperless/serialisers.py b/src/paperless/serialisers.py index b724dd451..4c9ed5641 100644 --- a/src/paperless/serialisers.py +++ b/src/paperless/serialisers.py @@ -1,5 +1,6 @@ import logging +from allauth.socialaccount.models import SocialAccount from django.contrib.auth.models import Group from django.contrib.auth.models import Permission from django.contrib.auth.models import User @@ -105,10 +106,30 @@ class GroupSerializer(serializers.ModelSerializer): ) +class SocialAccountSerializer(serializers.ModelSerializer): + name = serializers.SerializerMethodField() + + class Meta: + model = SocialAccount + fields = ( + "id", + "provider", + "name", + ) + + def get_name(self, obj): + return obj.get_provider_account().to_str() + + class ProfileSerializer(serializers.ModelSerializer): email = serializers.EmailField(allow_null=False) password = ObfuscatedUserPasswordField(required=False, allow_null=False) auth_token = serializers.SlugRelatedField(read_only=True, slug_field="key") + social_accounts = SocialAccountSerializer( + many=True, + read_only=True, + source="socialaccount_set", + ) class Meta: model = User @@ -118,6 +139,8 @@ class ProfileSerializer(serializers.ModelSerializer): "first_name", "last_name", "auth_token", + "social_accounts", + "has_usable_password", ) diff --git a/src/paperless/settings.py b/src/paperless/settings.py index d485415ca..d51ba9020 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -303,6 +303,9 @@ INSTALLED_APPS = [ "django_filters", "django_celery_results", "guardian", + "allauth", + "allauth.account", + "allauth.socialaccount", *env_apps, ] @@ -339,6 +342,7 @@ MIDDLEWARE = [ "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", + "allauth.account.middleware.AccountMiddleware", ] # Optional to enable compression @@ -350,6 +354,7 @@ ROOT_URLCONF = "paperless.urls" FORCE_SCRIPT_NAME = os.getenv("PAPERLESS_FORCE_SCRIPT_NAME") BASE_URL = (FORCE_SCRIPT_NAME or "") + "/" LOGIN_URL = BASE_URL + "accounts/login/" +LOGIN_REDIRECT_URL = "/dashboard" LOGOUT_REDIRECT_URL = os.getenv("PAPERLESS_LOGOUT_REDIRECT_URL") WSGI_APPLICATION = "paperless.wsgi.application" @@ -410,8 +415,28 @@ CHANNEL_LAYERS = { AUTHENTICATION_BACKENDS = [ "guardian.backends.ObjectPermissionBackend", "django.contrib.auth.backends.ModelBackend", + "allauth.account.auth_backends.AuthenticationBackend", ] +ACCOUNT_LOGOUT_ON_GET = True +ACCOUNT_DEFAULT_HTTP_PROTOCOL = os.getenv( + "PAPERLESS_ACCOUNT_DEFAULT_HTTP_PROTOCOL", + "https", +) + +ACCOUNT_ADAPTER = "paperless.adapter.CustomAccountAdapter" +ACCOUNT_ALLOW_SIGNUPS = __get_boolean("PAPERLESS_ACCOUNT_ALLOW_SIGNUPS") + +SOCIALACCOUNT_ADAPTER = "paperless.adapter.CustomSocialAccountAdapter" +SOCIALACCOUNT_ALLOW_SIGNUPS = __get_boolean( + "PAPERLESS_SOCIALACCOUNT_ALLOW_SIGNUPS", + "yes", +) +SOCIALACCOUNT_AUTO_SIGNUP = __get_boolean("PAPERLESS_SOCIAL_AUTO_SIGNUP") +SOCIALACCOUNT_PROVIDERS = json.loads( + os.getenv("PAPERLESS_SOCIALACCOUNT_PROVIDERS", "{}"), +) + AUTO_LOGIN_USERNAME = os.getenv("PAPERLESS_AUTO_LOGIN_USERNAME") if AUTO_LOGIN_USERNAME: diff --git a/src/paperless/tests/test_adapter.py b/src/paperless/tests/test_adapter.py new file mode 100644 index 000000000..ca79cbce0 --- /dev/null +++ b/src/paperless/tests/test_adapter.py @@ -0,0 +1,43 @@ +from allauth.account.adapter import get_adapter +from allauth.socialaccount.adapter import get_adapter as get_social_adapter +from django.conf import settings +from django.test import TestCase +from django.urls import reverse + + +class TestCustomAccountAdapter(TestCase): + def test_is_open_for_signup(self): + adapter = get_adapter() + + # Test when ACCOUNT_ALLOW_SIGNUPS is True + settings.ACCOUNT_ALLOW_SIGNUPS = True + self.assertTrue(adapter.is_open_for_signup(None)) + + # Test when ACCOUNT_ALLOW_SIGNUPS is False + settings.ACCOUNT_ALLOW_SIGNUPS = False + self.assertFalse(adapter.is_open_for_signup(None)) + + +class TestCustomSocialAccountAdapter(TestCase): + def test_is_open_for_signup(self): + adapter = get_social_adapter() + + # Test when SOCIALACCOUNT_ALLOW_SIGNUPS is True + settings.SOCIALACCOUNT_ALLOW_SIGNUPS = True + self.assertTrue(adapter.is_open_for_signup(None, None)) + + # Test when SOCIALACCOUNT_ALLOW_SIGNUPS is False + settings.SOCIALACCOUNT_ALLOW_SIGNUPS = False + self.assertFalse(adapter.is_open_for_signup(None, None)) + + def test_get_connect_redirect_url(self): + adapter = get_social_adapter() + request = None + socialaccount = None + + # Test the default URL + expected_url = reverse("base") + self.assertEqual( + adapter.get_connect_redirect_url(request, socialaccount), + expected_url, + ) diff --git a/src/paperless/urls.py b/src/paperless/urls.py index d45a7bf22..74f6fc108 100644 --- a/src/paperless/urls.py +++ b/src/paperless/urls.py @@ -41,10 +41,12 @@ from documents.views import WorkflowTriggerViewSet from documents.views import WorkflowViewSet from paperless.consumers import StatusConsumer from paperless.views import ApplicationConfigurationViewSet +from paperless.views import DisconnectSocialAccountView from paperless.views import FaviconView from paperless.views import GenerateAuthTokenView from paperless.views import GroupViewSet from paperless.views import ProfileView +from paperless.views import SocialAccountProvidersView from paperless.views import UserViewSet from paperless_mail.views import MailAccountTestView from paperless_mail.views import MailAccountViewSet @@ -132,6 +134,14 @@ urlpatterns = [ name="bulk_edit_object_permissions", ), path("profile/generate_auth_token/", GenerateAuthTokenView.as_view()), + path( + "profile/disconnect_social_account/", + DisconnectSocialAccountView.as_view(), + ), + path( + "profile/social_account_providers/", + SocialAccountProvidersView.as_view(), + ), re_path( "^profile/", ProfileView.as_view(), @@ -192,7 +202,7 @@ urlpatterns = [ ), # TODO: with localization, this is even worse! :/ # login, logout - path("accounts/", include("django.contrib.auth.urls")), + path("accounts/", include("allauth.urls")), # Root of the Frontend re_path( r".*", diff --git a/src/paperless/views.py b/src/paperless/views.py index 0f417b9ab..1151ceed5 100644 --- a/src/paperless/views.py +++ b/src/paperless/views.py @@ -1,10 +1,13 @@ import os from collections import OrderedDict +from allauth.socialaccount.adapter import get_adapter +from allauth.socialaccount.models import SocialAccount from django.contrib.auth.models import Group from django.contrib.auth.models import User from django.db.models.functions import Lower from django.http import HttpResponse +from django.http import HttpResponseBadRequest from django.views.generic import View from django_filters.rest_framework import DjangoFilterBackend from rest_framework.authtoken.models import Token @@ -14,6 +17,7 @@ from rest_framework.pagination import PageNumberPagination from rest_framework.permissions import DjangoObjectPermissions from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response +from rest_framework.views import APIView from rest_framework.viewsets import ModelViewSet from documents.permissions import PaperlessObjectPermissions @@ -168,3 +172,54 @@ class ApplicationConfigurationViewSet(ModelViewSet): serializer_class = ApplicationConfigurationSerializer permission_classes = (IsAuthenticated, DjangoObjectPermissions) + + +class DisconnectSocialAccountView(GenericAPIView): + """ + Disconnects a social account provider from the user account + """ + + permission_classes = [IsAuthenticated] + + def post(self, request, *args, **kwargs): + user = self.request.user + + try: + account = user.socialaccount_set.get(pk=request.data["id"]) + account_id = account.id + account.delete() + return Response(account_id) + except SocialAccount.DoesNotExist: + return HttpResponseBadRequest("Social account not found") + + +class SocialAccountProvidersView(APIView): + """ + List of social account providers + """ + + permission_classes = [IsAuthenticated] + + def get(self, request, *args, **kwargs): + adapter = get_adapter() + providers = adapter.list_providers(request) + resp = [ + {"name": p.name, "login_url": p.get_login_url(request, process="connect")} + for p in providers + if p.id != "openid" + ] + + for openid_provider in filter(lambda p: p.id == "openid", providers): + resp += [ + { + "name": b["name"], + "login_url": openid_provider.get_login_url( + request, + process="connect", + openid=b["openid_url"], + ), + } + for b in openid_provider.get_brands() + ] + + return Response(sorted(resp, key=lambda p: p["name"])) From b60e16fe335d401101966b9638955e6e0a3c175c Mon Sep 17 00:00:00 2001 From: Trenton H <797416+stumpylog@users.noreply.github.com> Date: Thu, 8 Feb 2024 09:48:24 -0800 Subject: [PATCH 34/41] Chore: Backend dependencies update (#5676) --- .github/workflows/ci.yml | 2 +- .pre-commit-config.yaml | 4 +- .ruff.toml | 20 +- Dockerfile | 2 +- Pipfile | 6 +- Pipfile.lock | 1192 +++++++++++++++------------- src/documents/serialisers.py | 27 +- src/documents/signals/handlers.py | 24 +- src/documents/views.py | 24 +- src/paperless/settings.py | 2 +- src/paperless/wsgi.py | 1 + src/paperless_mail/admin.py | 2 - src/paperless_mail/mail.py | 8 +- src/paperless_tesseract/parsers.py | 6 +- 14 files changed, 718 insertions(+), 602 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e19d03a4..1d298fc2f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ on: env: # This is the version of pipenv all the steps will use # If changing this, change Dockerfile - DEFAULT_PIP_ENV_VERSION: "2023.11.15" + DEFAULT_PIP_ENV_VERSION: "2023.12.1" # This is the default version of Python to use in most steps which aren't specific DEFAULT_PYTHON_VERSION: "3.10" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f2919e4a7..8073cc721 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -47,11 +47,11 @@ repos: exclude: "(^Pipfile\\.lock$)" # Python hooks - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 'v0.1.11' + rev: 'v0.2.1' hooks: - id: ruff - repo: https://github.com/psf/black-pre-commit-mirror - rev: 23.12.1 + rev: 24.1.1 hooks: - id: black # Dockerfile hooks diff --git a/.ruff.toml b/.ruff.toml index 497d3f1eb..6331bfaf5 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -1,5 +1,14 @@ +fix = true +line-length = 88 +respect-gitignore = true +src = ["src"] +target-version = "py39" +output-format = "grouped" +show-fixes = true + # https://docs.astral.sh/ruff/settings/ # https://docs.astral.sh/ruff/rules/ +[lint] extend-select = [ "W", # https://docs.astral.sh/ruff/rules/#pycodestyle-e-w "I", # https://docs.astral.sh/ruff/rules/#isort-i @@ -25,15 +34,8 @@ extend-select = [ ] # TODO PTH https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth ignore = ["DJ001", "SIM105", "RUF012"] -fix = true -line-length = 88 -respect-gitignore = true -src = ["src"] -target-version = "py39" -output-format = "grouped" -show-fixes = true -[per-file-ignores] +[lint.per-file-ignores] ".github/scripts/*.py" = ["E501", "INP001", "SIM117"] "docker/wait-for-redis.py" = ["INP001", "T201"] "*/tests/*.py" = ["E501", "SIM117"] @@ -41,5 +43,5 @@ show-fixes = true "src/paperless_tesseract/tests/test_parser.py" = ["RUF001"] "src/documents/models.py" = ["SIM115"] -[isort] +[lint.isort] force-single-line = true diff --git a/Dockerfile b/Dockerfile index ea5bd6026..e113f975c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,7 +29,7 @@ COPY Pipfile* ./ RUN set -eux \ && echo "Installing pipenv" \ - && python3 -m pip install --no-cache-dir --upgrade pipenv==2023.11.15 \ + && python3 -m pip install --no-cache-dir --upgrade pipenv==2023.12.1 \ && echo "Generating requirement.txt" \ && pipenv requirements > requirements.txt diff --git a/Pipfile b/Pipfile index 6d5c7f861..5cf814e85 100644 --- a/Pipfile +++ b/Pipfile @@ -7,7 +7,7 @@ name = "pypi" dateparser = "~=1.2" # WARNING: django does not use semver. # Only patch versions are guaranteed to not introduce breaking changes. -django = "~=4.2.9" +django = "~=4.2.10" django-allauth = "*" django-auditlog = "*" django-celery-results = "*" @@ -46,11 +46,11 @@ python-magic = "*" pyzbar = "*" rapidfuzz = "*" redis = {extras = ["hiredis"], version = "*"} -scikit-learn = "~=1.3" +scikit-learn = "~=1.4" setproctitle = "*" tika-client = "*" tqdm = "*" -uvicorn = {extras = ["standard"], version = "*"} +uvicorn = {extras = ["standard"], version = "==0.25.0"} watchdog = "~=3.0" whitenoise = "~=6.6" whoosh="~=2.7" diff --git a/Pipfile.lock b/Pipfile.lock index d177517c2..35d33c2a7 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "96529f734f51e6c41b88b81585d28de7ab9d3b6d3de33ec28d7daa0de6a40019" + "sha256": "3b7d7fbb271204d23f9cf66d0bdc9e77f5f995fccc2351816043d63b4dbec019" }, "pipfile-spec": 6, "requires": {}, @@ -164,11 +164,11 @@ }, "certifi": { "hashes": [ - "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", - "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474" + "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", + "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1" ], "markers": "python_version >= '3.6'", - "version": "==2023.11.17" + "version": "==2024.2.2" }, "cffi": { "hashes": [ @@ -225,7 +225,7 @@ "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" ], - "markers": "python_version >= '3.8'", + "markers": "platform_python_implementation != 'PyPy'", "version": "==1.16.0" }, "channels": { @@ -392,32 +392,41 @@ }, "cryptography": { "hashes": [ - "sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960", - "sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a", - "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc", - "sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a", - "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf", - "sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1", - "sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39", - "sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406", - "sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a", - "sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a", - "sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c", - "sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be", - "sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15", - "sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2", - "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d", - "sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157", - "sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003", - "sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248", - "sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a", - "sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec", - "sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309", - "sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7", - "sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d" + "sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380", + "sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589", + "sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea", + "sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65", + "sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a", + "sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3", + "sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008", + "sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1", + "sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2", + "sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635", + "sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2", + "sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90", + "sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee", + "sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a", + "sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242", + "sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12", + "sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2", + "sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d", + "sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be", + "sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee", + "sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6", + "sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529", + "sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929", + "sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1", + "sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6", + "sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a", + "sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446", + "sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9", + "sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888", + "sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4", + "sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33", + "sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f" ], "markers": "python_version >= '3.7'", - "version": "==41.0.7" + "version": "==42.0.2" }, "dateparser": { "hashes": [ @@ -428,6 +437,14 @@ "markers": "python_version >= '3.7'", "version": "==1.2.0" }, + "defusedxml": { + "hashes": [ + "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", + "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==0.7.1" + }, "deprecated": { "hashes": [ "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c", @@ -445,20 +462,20 @@ }, "django": { "hashes": [ - "sha256:12498cc3cb8bc8038539fef9e90e95f507502436c1f0c3a673411324fa675d14", - "sha256:2cc2fc7d1708ada170ddd6c99f35cc25db664f165d3794bc7723f46b2f8c8984" + "sha256:a2d4c4d4ea0b6f0895acde632071aff6400bfc331228fc978b05452a0ff3e9f1", + "sha256:b1260ed381b10a11753c73444408e19869f3241fc45c985cd55a30177c789d13" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.2.9" + "version": "==4.2.10" }, "django-allauth": { "hashes": [ - "sha256:ec19efb80b34d2f18bd831eab9b10b6301f58d1cce9f39af35f497b7e5b0a141" + "sha256:e449d2378e1f33803bcf6f1ecc28439ce383de9b5c128a66aa301c4a0e71806f" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==0.59.0" + "version": "==0.61.0" }, "django-auditlog": { "hashes": [ @@ -859,11 +876,11 @@ }, "kombu": { "hashes": [ - "sha256:0bb2e278644d11dea6272c17974a3dbb9688a949f3bb60aeb5b791329c44fadc", - "sha256:63bb093fc9bb80cfb3a0972336a5cec1fa7ac5f9ef7e8237c6bf8dda9469313e" + "sha256:0eac1bbb464afe6fb0924b21bf79460416d25d8abc52546d4f16cad94f789488", + "sha256:30e470f1a6b49c70dc6f6d13c3e4cc4e178aa6c469ceb6bcd55645385fc84b93" ], "markers": "python_version >= '3.8'", - "version": "==5.3.4" + "version": "==5.3.5" }, "langdetect": { "hashes": [ @@ -969,7 +986,6 @@ "sha256:fd814847901df6e8de13ce69b84c31fc9b3fb591224d6762d0b256d510cbf382", "sha256:fdb325b7fba1e2c40b9b1db407f85642e32404131c08480dd652110fc908561b" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==4.9.4" }, "markdown-it-py": { @@ -1052,19 +1068,19 @@ }, "mysqlclient": { "hashes": [ - "sha256:1f8889cc5f0141bb307b915e981a66793df663ace92259344661084a7dd8d12a", - "sha256:2c7ad15b87293b12fd44b47c46879ec95ec647f4567e866ccd70b8337584e9b2", - "sha256:45600f4f321096bd1ead3355bc62cfcf8d97dc78df94e4ab5db72ecb5db1bd04", - "sha256:4fabe1f4b545ed6244ad0ff426e6b27054b7e5c5b1392be0de2e5f2f59be0392", - "sha256:641a7c9de443ddef186a0e89f24b4251ad44f4ddc5e7094332bf2d286d7c9e33", - "sha256:8f40c872f19639366e3df27bef2ff087be0e3ee0bd3453470bd29f46b54a90f6", - "sha256:97eee76818774bb695e018ff4c3dafaab74b9a0b0cf32c90b02caeec3b19cd8e", - "sha256:9db6305cdf2a1da350f827d2a19be7f2666eafd9eb8d4f7cbbac5df847d61b99", - "sha256:c5a293baebbfcfa2905545198a54e90f1cf00f211eae6637d24930abb6432cba" + "sha256:329e4eec086a2336fe3541f1ce095d87a6f169d1cc8ba7b04ac68bcb234c9711", + "sha256:33bc9fb3464e7d7c10b1eaf7336c5ff8f2a3d3b88bab432116ad2490beb3bf41", + "sha256:3c318755e06df599338dad7625f884b8a71fcf322a9939ef78c9b3db93e1de7a", + "sha256:4e80dcad884dd6e14949ac6daf769123223a52a6805345608bf49cdaf7bc8b3a", + "sha256:9d3310295cb682232cadc28abd172f406c718b9ada41d2371259098ae37779d3", + "sha256:9d4c015480c4a6b2b1602eccd9846103fc70606244788d04aa14b31c4bd1f0e2", + "sha256:ac44777eab0a66c14cb0d38965572f762e193ec2e5c0723bcd11319cc5b693c5", + "sha256:d43987bb9626096a302ca6ddcdd81feaeca65ced1d5fe892a6a66b808326aa54", + "sha256:e1ebe3f41d152d7cb7c265349fdb7f1eca86ccb0ca24a90036cde48e00ceb2ab" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==2.2.1" + "version": "==2.2.4" }, "nltk": { "hashes": [ @@ -1077,45 +1093,53 @@ }, "numpy": { "hashes": [ - "sha256:02f98011ba4ab17f46f80f7f8f1c291ee7d855fcef0a5a98db80767a468c85cd", - "sha256:0b7e807d6888da0db6e7e75838444d62495e2b588b99e90dd80c3459594e857b", - "sha256:12c70ac274b32bc00c7f61b515126c9205323703abb99cd41836e8125ea0043e", - "sha256:1666f634cb3c80ccbd77ec97bc17337718f56d6658acf5d3b906ca03e90ce87f", - "sha256:18c3319a7d39b2c6a9e3bb75aab2304ab79a811ac0168a671a62e6346c29b03f", - "sha256:211ddd1e94817ed2d175b60b6374120244a4dd2287f4ece45d49228b4d529178", - "sha256:21a9484e75ad018974a2fdaa216524d64ed4212e418e0a551a2d83403b0531d3", - "sha256:39763aee6dfdd4878032361b30b2b12593fb445ddb66bbac802e2113eb8a6ac4", - "sha256:3c67423b3703f8fbd90f5adaa37f85b5794d3366948efe9a5190a5f3a83fc34e", - "sha256:46f47ee566d98849323f01b349d58f2557f02167ee301e5e28809a8c0e27a2d0", - "sha256:51c7f1b344f302067b02e0f5b5d2daa9ed4a721cf49f070280ac202738ea7f00", - "sha256:5f24750ef94d56ce6e33e4019a8a4d68cfdb1ef661a52cdaee628a56d2437419", - "sha256:697df43e2b6310ecc9d95f05d5ef20eacc09c7c4ecc9da3f235d39e71b7da1e4", - "sha256:6d45b3ec2faed4baca41c76617fcdcfa4f684ff7a151ce6fc78ad3b6e85af0a6", - "sha256:77810ef29e0fb1d289d225cabb9ee6cf4d11978a00bb99f7f8ec2132a84e0166", - "sha256:7ca4f24341df071877849eb2034948459ce3a07915c2734f1abb4018d9c49d7b", - "sha256:7f784e13e598e9594750b2ef6729bcd5a47f6cfe4a12cca13def35e06d8163e3", - "sha256:806dd64230dbbfaca8a27faa64e2f414bf1c6622ab78cc4264f7f5f028fee3bf", - "sha256:867e3644e208c8922a3be26fc6bbf112a035f50f0a86497f98f228c50c607bb2", - "sha256:8c66d6fec467e8c0f975818c1796d25c53521124b7cfb760114be0abad53a0a2", - "sha256:8ed07a90f5450d99dad60d3799f9c03c6566709bd53b497eb9ccad9a55867f36", - "sha256:9bc6d1a7f8cedd519c4b7b1156d98e051b726bf160715b769106661d567b3f03", - "sha256:9e1591f6ae98bcfac2a4bbf9221c0b92ab49762228f38287f6eeb5f3f55905ce", - "sha256:9e87562b91f68dd8b1c39149d0323b42e0082db7ddb8e934ab4c292094d575d6", - "sha256:a7081fd19a6d573e1a05e600c82a1c421011db7935ed0d5c483e9dd96b99cf13", - "sha256:a8474703bffc65ca15853d5fd4d06b18138ae90c17c8d12169968e998e448bb5", - "sha256:af36e0aa45e25c9f57bf684b1175e59ea05d9a7d3e8e87b7ae1a1da246f2767e", - "sha256:b1240f767f69d7c4c8a29adde2310b871153df9b26b5cb2b54a561ac85146485", - "sha256:b4d362e17bcb0011738c2d83e0a65ea8ce627057b2fdda37678f4374a382a137", - "sha256:b831295e5472954104ecb46cd98c08b98b49c69fdb7040483aff799a755a7374", - "sha256:b8c275f0ae90069496068c714387b4a0eba5d531aace269559ff2b43655edd58", - "sha256:bdd2b45bf079d9ad90377048e2747a0c82351989a2165821f0c96831b4a2a54b", - "sha256:cc0743f0302b94f397a4a65a660d4cd24267439eb16493fb3caad2e4389bccbb", - "sha256:da4b0c6c699a0ad73c810736303f7fbae483bcb012e38d7eb06a5e3b432c981b", - "sha256:f25e2811a9c932e43943a2615e65fc487a0b6b49218899e62e426e7f0a57eeda", - "sha256:f73497e8c38295aaa4741bdfa4fda1a5aedda5473074369eca10626835445511" + "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b", + "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818", + "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20", + "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0", + "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", + "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", + "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea", + "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c", + "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", + "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110", + "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be", + "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a", + "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", + "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5", + "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed", + "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd", + "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c", + "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", + "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0", + "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c", + "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a", + "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b", + "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", + "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6", + "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", + "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a", + "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30", + "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218", + "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", + "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", + "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2", + "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", + "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764", + "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", + "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3", + "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f" ], "markers": "python_version >= '3.9'", - "version": "==1.26.3" + "version": "==1.26.4" + }, + "oauthlib": { + "hashes": [ + "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", + "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918" + ], + "markers": "python_version >= '3.6'", + "version": "==3.2.2" }, "ocrmypdf": { "hashes": [ @@ -1161,41 +1185,41 @@ }, "pikepdf": { "hashes": [ - "sha256:169ed5a76e0eaa73cae9578b701d8e2176caa6fa2f24500cad81cf6923bd5803", - "sha256:1d9dfda022e0b44e737a99103cb8bf51f647044fc31500dbbef540c58808ed28", - "sha256:29d02d671a83730d55b00caccd9069e68efa58ed42ae968a0b2ae4954020c939", - "sha256:348499fd509c1d36114cdbbf19231000728314eb6b309a401d9787a36823cc5b", - "sha256:3aa0c6bc03da4f1d362227fa0a2645507668534f31e3f2f83d9ff5243cd9ef2f", - "sha256:4dd2ee67aa82b9e66f2e5e07530989340a8c533e9bddda175a7c1c1dc7ea9ae1", - "sha256:4e0af3466639188f14fcfcba137a6fd01df85990a1719018daab1babca3c8dbc", - "sha256:526054594973581b4c3a43acd7a25fe30b9c01ae328dbbe4c0c119e4da89e2f5", - "sha256:557deff410e9ffc8d222d42a9b3ee0eae672863ac2071eb14e96959ae39474b0", - "sha256:58acb7232c583af25454e661ea84b170b1cc8bb9f8758855dff2848e44d30190", - "sha256:5b76b37d887f6440a8c9bea5f7478666990225e54f71252a3bbaa41a2245f754", - "sha256:644e389bd0fe80bccb7763283e2912368659c655c3ebc6b6e45c116ceeaaee74", - "sha256:645620670f8a64e18a3de102a9a4b71618044b12b618901eed6bd9a3f5d10995", - "sha256:74c465d0030b90b70bebdd0f5442e01658eb98f078be2a5edb7f71d93b66d9b4", - "sha256:75217985f9448f7681801149dab71e24a1925bf95b9206b823e9ea3c3d8d24ba", - "sha256:7a6ec16703936864e4a625f31a0ba0f177b5784ab092158d340b0c6910bdff1e", - "sha256:937fe9b74b9bc3f0dccaf823ef3b58740d398f4a0694f035d7d34e2225fb5e6d", - "sha256:a91ada55df47c54b7d1404b3fab602ba824777732bad5250edb6b72708656848", - "sha256:ad12a1b2ed96903b020abf93c1140567c9c96508962dbc85168f63380c6fab67", - "sha256:aee897e598002a471a617221bdbd9e7783dd12bab533ef80d600617ce1257995", - "sha256:b101a438203c0f14a5177ac75e006ac3f0e923888338e2c1a52a7c01ecc62100", - "sha256:bab539e596dfdbddcc59a580b4b68fd024f81775e299bc866da5ae74baa55836", - "sha256:c264d8634e5f25f1033897fc366f8c6923106d561f880a3898a9759c69d0c1f1", - "sha256:d2e591d3eec993b8fbd32800b6655d6683d8e2505d6c63acd8700b8ccbb554b5", - "sha256:d35c9371b6f5991968c55816aa2caa1970f51761feb7bd9a1a28fc94a785c9d3", - "sha256:debb76598d82120c85d1318e1f603e731d8f1c1c7ffd5d83fdfdd11220227494", - "sha256:e4d6ef15bda29f26fb1de1a0dd67431becb456e597e6f1e4408927f98e27eeac", - "sha256:ea8ee96eae922e54c0f22b6bfe8b406ae58ec77a735612c96e1f33c940c5961a", - "sha256:ec72e5e90f9cb59e2ea8cd2a3b0c7d7adacc059579dbd25debaa6a0feb8418ae", - "sha256:f0f29e94b6702deab7644feb1a61af8228a23879e52003205dc0f54fa1ce4e18", - "sha256:f55f2c170d7e7e125f0d691a9b3080e1cec50d31a9dd1ba75d96a3ce0df92e1c", - "sha256:fb9899bc0b4bb30440b6a622c287dea38b69dbc0a85cf890c43e107e07525e64" + "sha256:0586b9aec4f78a4f6ab2497b47639744ce47b7ad9b96e149194b7ada2d34b848", + "sha256:0718f822dd75574e8c015983d870d8336fe7e62d20f1f726e86fc3156d62c921", + "sha256:0c7b1404f9c7714377c3df7c756cff03ac237b2bf3273c1193d0a1849e1b83f0", + "sha256:119c18de07040cbbdf9f06a3a06fe89c8f321d3386fb96b2f68455bb2f7cab46", + "sha256:19867ff1c4db1da7a74aad0fc9455a6ca1b7a45ee460c1e7fce1dd1962c0251d", + "sha256:238cce9d9bbda606eb7050096251753b32f84c758633212d62ed5f1ecf8f5091", + "sha256:32b033345d0979e8a0dfd7c3af76013b3a3632da50d419545d012535f0cfd3e1", + "sha256:4802e48046a059b26949b16c53adb9ce8c0137a265cb8b1083957a3fadc0418b", + "sha256:480c69fabe958b56843dc38953c8568a4da2cc819d88b3b1080a470c114e622a", + "sha256:485760bb22bcf25f338ab7dc003e6b5f522783f5f00bab0079ea3383152b2d98", + "sha256:54eeaeaa4bdca7320511f00f403ea1ae33ef2e006fd5b5c2b92d92ab7eb77641", + "sha256:5f09c4263bfcad2ba9836b578c42218d6d8cf751331f5cc9639b68d60ab7a05d", + "sha256:711064313c00a5dccc5306877844f4e12b33355a9f496d870158d7f906330fd2", + "sha256:7209d3cb07d238ef81d438644363a19974697ab6c0d3360292c3c1cb4f2915e6", + "sha256:73fdaa359262feda7f62aa87bfb97c66f9a30d80bcb592d2c4c5e7714f2ab30e", + "sha256:744903f2a34f8feeb781e3e5ca86b3ea60aae92246c9085392666eaa878ca7fc", + "sha256:75de10328d6558e7821aadabb63cadbb40eec3cdc407b01ef990760343ad4305", + "sha256:78f0d79548e56636deb4749fb3fa73458dcdf86e634a1ce1b6e5155f97c4155f", + "sha256:7fd20e259c2192ca1b718d8f78bbeff1b37340b6a2c9764068c2112449895d85", + "sha256:8bc82d766f09fafd986d9410c7d53345b3e2963fe3253a9ba09b50812b60e9c4", + "sha256:93ba53c05d511a5e8a1d5ccf00b71a3c89e9285ec92e8f9a86995b022e667346", + "sha256:98c04283649b09c8c8d5f18389787dfe48224a3b99758d984ace9c1f1940dd33", + "sha256:9b2f8f518d7d4ee374deccc27cb65494c33cbc600885253e4454ab31dc466a04", + "sha256:9f2cff2ed901b5dd6ea704a24e88dfb9e3303019473ffc224d4d72a294e7c43b", + "sha256:b2d9957e188785a5913deedf6f12dc7eafb5764a3cc3996e044f0e9044b28889", + "sha256:b879e240b4d4b34822a0390fe4f30e6388d0f776e12376f043dc102fe11e9499", + "sha256:b8a91610cf07d29ef06992cdfae902fecef3712100f25796c553e1e3a0553a33", + "sha256:c1e13a4f4c7a596ad4157b93453de4dfcb7abc14c58ce9ee110b0e2ee7a0813e", + "sha256:c60e8b28b9e96137adc79eade42a764ff508291270e99ac84290808a85a12d94", + "sha256:d9ee83f20559c63413fed0059ea159a11116af098f976b8480e82018d954bba9", + "sha256:ea368bb11c1b57b773f349e8a1283617a66227a628f94023bb4a16ed53a1adf3", + "sha256:fc8dcbdb7ba95ef1e8318eb2ebd2869d53e9e9f8b81d9d7b7b5f9acbc4e6557b" ], "markers": "python_version >= '3.8'", - "version": "==8.11.2" + "version": "==8.12.0" }, "pillow": { "hashes": [ @@ -1273,11 +1297,11 @@ }, "pluggy": { "hashes": [ - "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12", - "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7" + "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981", + "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be" ], "markers": "python_version >= '3.8'", - "version": "==1.3.0" + "version": "==1.4.0" }, "portalocker": { "hashes": [ @@ -1338,6 +1362,17 @@ "markers": "python_version >= '3.7'", "version": "==2.17.2" }, + "pyjwt": { + "extras": [ + "crypto" + ], + "hashes": [ + "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de", + "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320" + ], + "markers": "python_version >= '3.7'", + "version": "==2.8.0" + }, "python-dateutil": { "hashes": [ "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", @@ -1349,12 +1384,12 @@ }, "python-dotenv": { "hashes": [ - "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba", - "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a" + "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", + "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.0.0" + "version": "==1.0.1" }, "python-gnupg": { "hashes": [ @@ -1382,12 +1417,19 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==0.4.27" }, + "python3-openid": { + "hashes": [ + "sha256:33fbf6928f401e0b790151ed2b5290b02545e8775f982485205a066f874aaeaf", + "sha256:6626f771e0417486701e0b4daff762e7212e820ca5b29fcc0d05f6f8736dfa6b" + ], + "version": "==3.2.0" + }, "pytz": { "hashes": [ - "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b", - "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7" + "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812", + "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319" ], - "version": "==2023.3.post1" + "version": "==2024.1" }, "pyyaml": { "hashes": [ @@ -1420,6 +1462,7 @@ "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", + "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef", "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", @@ -1662,11 +1705,27 @@ }, "reportlab": { "hashes": [ - "sha256:169945817a1a7759fb7b7dae528c2f8064fc21d16338d9b572ebdcb756740853", - "sha256:d00693de8ab8761b122e409de883ba976c24839f93867090c0d40b5d5906e847" + "sha256:c9656216321897486e323be138f7aea67851cedc116b8cc35f8ec7f8cc763538", + "sha256:f32bff66a0fda234202e1e33eaf77f25008871a61cb01cd91584a521a04c0047" ], "markers": "python_version >= '3.7' and python_version < '4'", - "version": "==4.0.8" + "version": "==4.0.9" + }, + "requests": { + "hashes": [ + "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", + "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" + ], + "markers": "python_version >= '3.7'", + "version": "==2.31.0" + }, + "requests-oauthlib": { + "hashes": [ + "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5", + "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.3.1" }, "rich": { "hashes": [ @@ -1678,67 +1737,82 @@ }, "scikit-learn": { "hashes": [ - "sha256:0402638c9a7c219ee52c94cbebc8fcb5eb9fe9c773717965c1f4185588ad3107", - "sha256:0ee107923a623b9f517754ea2f69ea3b62fc898a3641766cb7deb2f2ce450161", - "sha256:1215e5e58e9880b554b01187b8c9390bf4dc4692eedeaf542d3273f4785e342c", - "sha256:15e1e94cc23d04d39da797ee34236ce2375ddea158b10bee3c343647d615581d", - "sha256:18424efee518a1cde7b0b53a422cde2f6625197de6af36da0b57ec502f126157", - "sha256:1d08ada33e955c54355d909b9c06a4789a729977f165b8bae6f225ff0a60ec4a", - "sha256:3271552a5eb16f208a6f7f617b8cc6d1f137b52c8a1ef8edf547db0259b2c9fb", - "sha256:35a22e8015048c628ad099da9df5ab3004cdbf81edc75b396fd0cff8699ac58c", - "sha256:535805c2a01ccb40ca4ab7d081d771aea67e535153e35a1fd99418fcedd1648a", - "sha256:5b2de18d86f630d68fe1f87af690d451388bb186480afc719e5f770590c2ef6c", - "sha256:61a6efd384258789aa89415a410dcdb39a50e19d3d8410bd29be365bcdd512d5", - "sha256:64381066f8aa63c2710e6b56edc9f0894cc7bf59bd71b8ce5613a4559b6145e0", - "sha256:67f37d708f042a9b8d59551cf94d30431e01374e00dc2645fa186059c6c5d78b", - "sha256:6c43290337f7a4b969d207e620658372ba3c1ffb611f8bc2b6f031dc5c6d1d03", - "sha256:6fb6bc98f234fda43163ddbe36df8bcde1d13ee176c6dc9b92bb7d3fc842eb66", - "sha256:763f0ae4b79b0ff9cca0bf3716bcc9915bdacff3cebea15ec79652d1cc4fa5c9", - "sha256:785a2213086b7b1abf037aeadbbd6d67159feb3e30263434139c98425e3dcfcf", - "sha256:8db94cd8a2e038b37a80a04df8783e09caac77cbe052146432e67800e430c028", - "sha256:a19f90f95ba93c1a7f7924906d0576a84da7f3b2282ac3bfb7a08a32801add93", - "sha256:a2f54c76accc15a34bfb9066e6c7a56c1e7235dda5762b990792330b52ccfb05", - "sha256:b8692e395a03a60cd927125eef3a8e3424d86dde9b2370d544f0ea35f78a8073", - "sha256:cb06f8dce3f5ddc5dee1715a9b9f19f20d295bed8e3cd4fa51e1d050347de525", - "sha256:dc9002fc200bed597d5d34e90c752b74df516d592db162f756cc52836b38fe0e", - "sha256:e326c0eb5cf4d6ba40f93776a20e9a7a69524c4db0757e7ce24ba222471ee8a1", - "sha256:ed932ea780517b00dae7431e031faae6b49b20eb6950918eb83bd043237950e0", - "sha256:fc4144a5004a676d5022b798d9e573b05139e77f271253a4703eed295bde0433" + "sha256:05fc5915b716c6cc60a438c250108e9a9445b522975ed37e416d5ea4f9a63381", + "sha256:0aba2a20d89936d6e72d95d05e3bf1db55bca5c5920926ad7b92c34f5e7d3bbe", + "sha256:0db8e22c42f7980fe5eb22069b1f84c48966f3e0d23a01afde5999e3987a2501", + "sha256:0f33bbafb310c26b81c4d41ecaebdbc1f63498a3f13461d50ed9a2e8f24d28e4", + "sha256:11b3b140f70fbc9f6a08884631ae8dd60a4bb2d7d6d1de92738ea42b740d8992", + "sha256:1d041bc95006b545b59e458399e3175ab11ca7a03dc9a74a573ac891f5df1489", + "sha256:2404659fedec40eeafa310cd14d613e564d13dbf8f3c752d31c095195ec05de6", + "sha256:27ae4b0f1b2c77107c096a7e05b33458354107b47775428d1f11b23e30a73e8a", + "sha256:2b465dd1dcd237b7b1dcd1a9048ccbf70a98c659474324fa708464c3a2533fad", + "sha256:2bac5d56b992f8f06816f2cd321eb86071c6f6d44bb4b1cb3d626525820d754b", + "sha256:349669b01435bc4dbf25c6410b0892073befdaec52637d1a1d1ff53865dc8db3", + "sha256:53b9e29177897c37e2ff9d4ba6ca12fdb156e22523e463db05def303f5c72b5c", + "sha256:5c5c62ffb52c3ffb755eb21fa74cc2cbf2c521bd53f5c04eaa10011dbecf5f80", + "sha256:74812c9eabb265be69d738a8ea8d4884917a59637fcbf88a5f0e9020498bc6b3", + "sha256:76986d22e884ab062b1beecdd92379656e9d3789ecc1f9870923c178de55f9fe", + "sha256:785ce3c352bf697adfda357c3922c94517a9376002971bc5ea50896144bc8916", + "sha256:7f0d2018ac6fa055dab65fe8a485967990d33c672d55bc254c56c35287b02fab", + "sha256:80a21de63275f8bcd7877b3e781679d2ff1eddfed515a599f95b2502a3283d42", + "sha256:833999872e2920ce00f3a50839946bdac7539454e200eb6db54898a41f4bfd43", + "sha256:842b7d6989f3c574685e18da6f91223eb32301d0f93903dd399894250835a6f7", + "sha256:88bcb586fdff865372df1bc6be88bb7e6f9e0aa080dab9f54f5cac7eca8e2b6b", + "sha256:8b6ac1442ec714b4911e5aef8afd82c691b5c88b525ea58299d455acc4e8dcec", + "sha256:91a8918c415c4b4bf1d60c38d32958849a9191c2428ab35d30b78354085c7c7a", + "sha256:923d778f378ebacca2c672ab1740e5a413e437fb45ab45ab02578f8b689e5d43", + "sha256:970ec697accaef10fb4f51763f3a7b1250f9f0553cf05514d0e94905322a0172", + "sha256:a0e2427d9ef46477625ab9b55c1882844fe6fc500f418c3f8e650200182457bc", + "sha256:a6372c90bbf302387792108379f1ec77719c1618d88496d0df30cb8e370b4661", + "sha256:a8341eabdc754d5ab91641a7763243845e96b6d68e03e472531e88a4f1b09f21", + "sha256:aad2a63e0dd386b92da3270887a29b308af4d7c750d8c4995dfd9a4798691bcc", + "sha256:c408b46b2fd61952d519ea1af2f8f0a7a703e1433923ab1704c4131520b2083b", + "sha256:cb8f044a8f5962613ce1feb4351d66f8d784bd072d36393582f351859b065f7d", + "sha256:d1f6bce875ac2bb6b52514f67c185c564ccd299a05b65b7bab091a4c13dde12d", + "sha256:d3d75343940e7bf9b85c830c93d34039fa015eeb341c5c0b4cd7a90dadfe00d4", + "sha256:d4373c984eba20e393216edd51a3e3eede56cbe93d4247516d205643c3b93121", + "sha256:d439c584e58434d0350701bd33f6c10b309e851fccaf41c121aed55f6851d8cf", + "sha256:d77df3d1e15fc37a9329999979fa7868ba8655dbab21fe97fc7ddabac9e08cc7", + "sha256:e22446ad89f1cb7657f0d849dcdc345b48e2d10afa3daf2925fdb740f85b714c", + "sha256:e7eef6ea2ed289af40e88c0be9f7704ca8b5de18508a06897c3fe21e0905efdf", + "sha256:e98632da8f6410e6fb6bf66937712c949b4010600ccd3f22a5388a83e610cc3c", + "sha256:f77674647dd31f56cb12ed13ed25b6ed43a056fffef051715022d2ebffd7a7d1", + "sha256:fce93a7473e2f4ee4cc280210968288d6a7d7ad8dc6fa7bb7892145e407085f9" ], "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==1.3.2" + "markers": "python_version >= '3.9'", + "version": "==1.4.0" }, "scipy": { "hashes": [ - "sha256:00150c5eae7b610c32589dda259eacc7c4f1665aedf25d921907f4d08a951b1c", - "sha256:028eccd22e654b3ea01ee63705681ee79933652b2d8f873e7949898dda6d11b6", - "sha256:1b7c3dca977f30a739e0409fb001056484661cb2541a01aba0bb0029f7b68db8", - "sha256:2c6ff6ef9cc27f9b3db93a6f8b38f97387e6e0591600369a297a50a8e96e835d", - "sha256:36750b7733d960d7994888f0d148d31ea3017ac15eef664194b4ef68d36a4a97", - "sha256:530f9ad26440e85766509dbf78edcfe13ffd0ab7fec2560ee5c36ff74d6269ff", - "sha256:5e347b14fe01003d3b78e196e84bd3f48ffe4c8a7b8a1afbcb8f5505cb710993", - "sha256:6550466fbeec7453d7465e74d4f4b19f905642c89a7525571ee91dd7adabb5a3", - "sha256:6df1468153a31cf55ed5ed39647279beb9cfb5d3f84369453b49e4b8502394fd", - "sha256:6e619aba2df228a9b34718efb023966da781e89dd3d21637b27f2e54db0410d7", - "sha256:8fce70f39076a5aa62e92e69a7f62349f9574d8405c0a5de6ed3ef72de07f446", - "sha256:90a2b78e7f5733b9de748f589f09225013685f9b218275257f8a8168ededaeaa", - "sha256:91af76a68eeae0064887a48e25c4e616fa519fa0d38602eda7e0f97d65d57937", - "sha256:933baf588daa8dc9a92c20a0be32f56d43faf3d1a60ab11b3f08c356430f6e56", - "sha256:acf8ed278cc03f5aff035e69cb511741e0418681d25fbbb86ca65429c4f4d9cd", - "sha256:ad669df80528aeca5f557712102538f4f37e503f0c5b9541655016dd0932ca79", - "sha256:b030c6674b9230d37c5c60ab456e2cf12f6784596d15ce8da9365e70896effc4", - "sha256:b9999c008ccf00e8fbcce1236f85ade5c569d13144f77a1946bef8863e8f6eb4", - "sha256:bc9a714581f561af0848e6b69947fda0614915f072dfd14142ed1bfe1b806710", - "sha256:ce7fff2e23ab2cc81ff452a9444c215c28e6305f396b2ba88343a567feec9660", - "sha256:cf00bd2b1b0211888d4dc75656c0412213a8b25e80d73898083f402b50f47e41", - "sha256:d10e45a6c50211fe256da61a11c34927c68f277e03138777bdebedd933712fea", - "sha256:ee410e6de8f88fd5cf6eadd73c135020bfbbbdfcd0f6162c36a7638a1ea8cc65", - "sha256:f313b39a7e94f296025e3cffc2c567618174c0b1dde173960cf23808f9fae4be", - "sha256:f3cd9e7b3c2c1ec26364856f9fbe78695fe631150f94cd1c22228456404cf1ec" + "sha256:196ebad3a4882081f62a5bf4aeb7326aa34b110e533aab23e4374fcccb0890dc", + "sha256:408c68423f9de16cb9e602528be4ce0d6312b05001f3de61fe9ec8b1263cad08", + "sha256:4bf5abab8a36d20193c698b0f1fc282c1d083c94723902c447e5d2f1780936a3", + "sha256:4c1020cad92772bf44b8e4cdabc1df5d87376cb219742549ef69fc9fd86282dd", + "sha256:5adfad5dbf0163397beb4aca679187d24aec085343755fcdbdeb32b3679f254c", + "sha256:5e32847e08da8d895ce09d108a494d9eb78974cf6de23063f93306a3e419960c", + "sha256:6546dc2c11a9df6926afcbdd8a3edec28566e4e785b915e849348c6dd9f3f490", + "sha256:730badef9b827b368f351eacae2e82da414e13cf8bd5051b4bdfd720271a5371", + "sha256:75ea2a144096b5e39402e2ff53a36fecfd3b960d786b7efd3c180e29c39e53f2", + "sha256:78e4402e140879387187f7f25d91cc592b3501a2e51dfb320f48dfb73565f10b", + "sha256:8b8066bce124ee5531d12a74b617d9ac0ea59245246410e19bca549656d9a40a", + "sha256:8bee4993817e204d761dba10dbab0774ba5a8612e57e81319ea04d84945375ba", + "sha256:913d6e7956c3a671de3b05ccb66b11bc293f56bfdef040583a7221d9e22a2e35", + "sha256:95e5c750d55cf518c398a8240571b0e0782c2d5a703250872f36eaf737751338", + "sha256:9c39f92041f490422924dfdb782527a4abddf4707616e07b021de33467f917bc", + "sha256:a24024d45ce9a675c1fb8494e8e5244efea1c7a09c60beb1eeb80373d0fecc70", + "sha256:a7ebda398f86e56178c2fa94cad15bf457a218a54a35c2a7b4490b9f9cb2676c", + "sha256:b360f1b6b2f742781299514e99ff560d1fe9bd1bff2712894b52abe528d1fd1e", + "sha256:bba1b0c7256ad75401c73e4b3cf09d1f176e9bd4248f0d3112170fb2ec4db067", + "sha256:c3003652496f6e7c387b1cf63f4bb720951cfa18907e998ea551e6de51a04467", + "sha256:e53958531a7c695ff66c2e7bb7b79560ffdc562e2051644c5576c39ff8efb563", + "sha256:e646d8571804a304e1da01040d21577685ce8e2db08ac58e543eaca063453e1c", + "sha256:e7e76cc48638228212c747ada851ef355c2bb5e7f939e10952bc504c11f4e372", + "sha256:f5f00ebaf8de24d14b8449981a2842d404152774c1a1d880c901bf454cb8e2a1", + "sha256:f7ce148dffcd64ade37b2df9315541f9adad6efcaa86866ee7dd5db0c8f041c3" ], "markers": "python_version >= '3.9'", - "version": "==1.11.4" + "version": "==1.12.0" }, "setproctitle": { "hashes": [ @@ -1926,6 +2000,14 @@ "markers": "python_version >= '3.8'", "version": "==5.2" }, + "urllib3": { + "hashes": [ + "sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20", + "sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224" + ], + "markers": "python_version >= '3.8'", + "version": "==2.2.0" + }, "uvicorn": { "extras": [ "standard" @@ -2414,40 +2496,40 @@ }, "black": { "hashes": [ - "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50", - "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f", - "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e", - "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec", - "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055", - "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3", - "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5", - "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54", - "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b", - "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e", - "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e", - "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba", - "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea", - "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59", - "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d", - "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0", - "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9", - "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a", - "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e", - "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba", - "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2", - "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2" + "sha256:0269dfdea12442022e88043d2910429bed717b2d04523867a85dacce535916b8", + "sha256:07204d078e25327aad9ed2c64790d681238686bce254c910de640c7cc4fc3aa6", + "sha256:08b34e85170d368c37ca7bf81cf67ac863c9d1963b2c1780c39102187ec8dd62", + "sha256:1a95915c98d6e32ca43809d46d932e2abc5f1f7d582ffbe65a5b4d1588af7445", + "sha256:2588021038bd5ada078de606f2a804cadd0a3cc6a79cb3e9bb3a8bf581325a4c", + "sha256:2fa6a0e965779c8f2afb286f9ef798df770ba2b6cee063c650b96adec22c056a", + "sha256:34afe9da5056aa123b8bfda1664bfe6fb4e9c6f311d8e4a6eb089da9a9173bf9", + "sha256:3897ae5a21ca132efa219c029cce5e6bfc9c3d34ed7e892113d199c0b1b444a2", + "sha256:40657e1b78212d582a0edecafef133cf1dd02e6677f539b669db4746150d38f6", + "sha256:48b5760dcbfe5cf97fd4fba23946681f3a81514c6ab8a45b50da67ac8fbc6c7b", + "sha256:5242ecd9e990aeb995b6d03dc3b2d112d4a78f2083e5a8e86d566340ae80fec4", + "sha256:5cdc2e2195212208fbcae579b931407c1fa9997584f0a415421748aeafff1168", + "sha256:5d7b06ea8816cbd4becfe5f70accae953c53c0e53aa98730ceccb0395520ee5d", + "sha256:7258c27115c1e3b5de9ac6c4f9957e3ee2c02c0b39222a24dc7aa03ba0e986f5", + "sha256:854c06fb86fd854140f37fb24dbf10621f5dab9e3b0c29a690ba595e3d543024", + "sha256:a21725862d0e855ae05da1dd25e3825ed712eaaccef6b03017fe0853a01aa45e", + "sha256:a83fe522d9698d8f9a101b860b1ee154c1d25f8a82ceb807d319f085b2627c5b", + "sha256:b3d64db762eae4a5ce04b6e3dd745dcca0fb9560eb931a5be97472e38652a161", + "sha256:e298d588744efda02379521a19639ebcd314fba7a49be22136204d7ed1782717", + "sha256:e2c8dfa14677f90d976f68e0c923947ae68fa3961d61ee30976c388adc0b02c8", + "sha256:ecba2a15dfb2d97105be74bbfe5128bc5e9fa8477d8c46766505c1dda5883aac", + "sha256:fc1ec9aa6f4d98d022101e015261c056ddebe3da6a8ccfc2c792cbe0349d48b7" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==23.12.1" + "version": "==24.1.1" }, "certifi": { "hashes": [ - "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", - "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474" + "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", + "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1" ], "markers": "python_version >= '3.6'", - "version": "==2023.11.17" + "version": "==2024.2.2" }, "cffi": { "hashes": [ @@ -2504,7 +2586,7 @@ "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" ], - "markers": "python_version >= '3.8'", + "markers": "platform_python_implementation != 'PyPy'", "version": "==1.16.0" }, "cfgv": { @@ -2640,90 +2722,99 @@ "toml" ], "hashes": [ - "sha256:04387a4a6ecb330c1878907ce0dc04078ea72a869263e53c72a1ba5bbdf380ca", - "sha256:0676cd0ba581e514b7f726495ea75aba3eb20899d824636c6f59b0ed2f88c471", - "sha256:0e8d06778e8fbffccfe96331a3946237f87b1e1d359d7fbe8b06b96c95a5407a", - "sha256:0eb3c2f32dabe3a4aaf6441dde94f35687224dfd7eb2a7f47f3fd9428e421058", - "sha256:109f5985182b6b81fe33323ab4707011875198c41964f014579cf82cebf2bb85", - "sha256:13eaf476ec3e883fe3e5fe3707caeb88268a06284484a3daf8250259ef1ba143", - "sha256:164fdcc3246c69a6526a59b744b62e303039a81e42cfbbdc171c91a8cc2f9446", - "sha256:26776ff6c711d9d835557ee453082025d871e30b3fd6c27fcef14733f67f0590", - "sha256:26f66da8695719ccf90e794ed567a1549bb2644a706b41e9f6eae6816b398c4a", - "sha256:29f3abe810930311c0b5d1a7140f6395369c3db1be68345638c33eec07535105", - "sha256:316543f71025a6565677d84bc4df2114e9b6a615aa39fb165d697dba06a54af9", - "sha256:36b0ea8ab20d6a7564e89cb6135920bc9188fb5f1f7152e94e8300b7b189441a", - "sha256:3cc9d4bc55de8003663ec94c2f215d12d42ceea128da8f0f4036235a119c88ac", - "sha256:485e9f897cf4856a65a57c7f6ea3dc0d4e6c076c87311d4bc003f82cfe199d25", - "sha256:5040148f4ec43644702e7b16ca864c5314ccb8ee0751ef617d49aa0e2d6bf4f2", - "sha256:51456e6fa099a8d9d91497202d9563a320513fcf59f33991b0661a4a6f2ad450", - "sha256:53d7d9158ee03956e0eadac38dfa1ec8068431ef8058fe6447043db1fb40d932", - "sha256:5a10a4920def78bbfff4eff8a05c51be03e42f1c3735be42d851f199144897ba", - "sha256:5b14b4f8760006bfdb6e08667af7bc2d8d9bfdb648351915315ea17645347137", - "sha256:5b2ccb7548a0b65974860a78c9ffe1173cfb5877460e5a229238d985565574ae", - "sha256:697d1317e5290a313ef0d369650cfee1a114abb6021fa239ca12b4849ebbd614", - "sha256:6ae8c9d301207e6856865867d762a4b6fd379c714fcc0607a84b92ee63feff70", - "sha256:707c0f58cb1712b8809ece32b68996ee1e609f71bd14615bd8f87a1293cb610e", - "sha256:74775198b702868ec2d058cb92720a3c5a9177296f75bd97317c787daf711505", - "sha256:756ded44f47f330666843b5781be126ab57bb57c22adbb07d83f6b519783b870", - "sha256:76f03940f9973bfaee8cfba70ac991825611b9aac047e5c80d499a44079ec0bc", - "sha256:79287fd95585ed36e83182794a57a46aeae0b64ca53929d1176db56aacc83451", - "sha256:799c8f873794a08cdf216aa5d0531c6a3747793b70c53f70e98259720a6fe2d7", - "sha256:7d360587e64d006402b7116623cebf9d48893329ef035278969fa3bbf75b697e", - "sha256:80b5ee39b7f0131ebec7968baa9b2309eddb35b8403d1869e08f024efd883566", - "sha256:815ac2d0f3398a14286dc2cea223a6f338109f9ecf39a71160cd1628786bc6f5", - "sha256:83c2dda2666fe32332f8e87481eed056c8b4d163fe18ecc690b02802d36a4d26", - "sha256:846f52f46e212affb5bcf131c952fb4075b55aae6b61adc9856222df89cbe3e2", - "sha256:936d38794044b26c99d3dd004d8af0035ac535b92090f7f2bb5aa9c8e2f5cd42", - "sha256:9864463c1c2f9cb3b5db2cf1ff475eed2f0b4285c2aaf4d357b69959941aa555", - "sha256:995ea5c48c4ebfd898eacb098164b3cc826ba273b3049e4a889658548e321b43", - "sha256:a1526d265743fb49363974b7aa8d5899ff64ee07df47dd8d3e37dcc0818f09ed", - "sha256:a56de34db7b7ff77056a37aedded01b2b98b508227d2d0979d373a9b5d353daa", - "sha256:a7c97726520f784239f6c62506bc70e48d01ae71e9da128259d61ca5e9788516", - "sha256:b8e99f06160602bc64da35158bb76c73522a4010f0649be44a4e167ff8555952", - "sha256:bb1de682da0b824411e00a0d4da5a784ec6496b6850fdf8c865c1d68c0e318dd", - "sha256:bf477c355274a72435ceb140dc42de0dc1e1e0bf6e97195be30487d8eaaf1a09", - "sha256:bf635a52fc1ea401baf88843ae8708591aa4adff875e5c23220de43b1ccf575c", - "sha256:bfd5db349d15c08311702611f3dccbef4b4e2ec148fcc636cf8739519b4a5c0f", - "sha256:c530833afc4707fe48524a44844493f36d8727f04dcce91fb978c414a8556cc6", - "sha256:cc6d65b21c219ec2072c1293c505cf36e4e913a3f936d80028993dd73c7906b1", - "sha256:cd3c1e4cb2ff0083758f09be0f77402e1bdf704adb7f89108007300a6da587d0", - "sha256:cfd2a8b6b0d8e66e944d47cdec2f47c48fef2ba2f2dff5a9a75757f64172857e", - "sha256:d0ca5c71a5a1765a0f8f88022c52b6b8be740e512980362f7fdbb03725a0d6b9", - "sha256:e7defbb9737274023e2d7af02cac77043c86ce88a907c58f42b580a97d5bcca9", - "sha256:e9d1bf53c4c8de58d22e0e956a79a5b37f754ed1ffdbf1a260d9dcfa2d8a325e", - "sha256:ea81d8f9691bb53f4fb4db603203029643caffc82bf998ab5b59ca05560f4c06" + "sha256:0193657651f5399d433c92f8ae264aff31fc1d066deee4b831549526433f3f61", + "sha256:02f2edb575d62172aa28fe00efe821ae31f25dc3d589055b3fb64d51e52e4ab1", + "sha256:0491275c3b9971cdbd28a4595c2cb5838f08036bca31765bad5e17edf900b2c7", + "sha256:077d366e724f24fc02dbfe9d946534357fda71af9764ff99d73c3c596001bbd7", + "sha256:10e88e7f41e6197ea0429ae18f21ff521d4f4490aa33048f6c6f94c6045a6a75", + "sha256:18e961aa13b6d47f758cc5879383d27b5b3f3dcd9ce8cdbfdc2571fe86feb4dd", + "sha256:1a78b656a4d12b0490ca72651fe4d9f5e07e3c6461063a9b6265ee45eb2bdd35", + "sha256:1ed4b95480952b1a26d863e546fa5094564aa0065e1e5f0d4d0041f293251d04", + "sha256:23b27b8a698e749b61809fb637eb98ebf0e505710ec46a8aa6f1be7dc0dc43a6", + "sha256:23f5881362dcb0e1a92b84b3c2809bdc90db892332daab81ad8f642d8ed55042", + "sha256:32a8d985462e37cfdab611a6f95b09d7c091d07668fdc26e47a725ee575fe166", + "sha256:3468cc8720402af37b6c6e7e2a9cdb9f6c16c728638a2ebc768ba1ef6f26c3a1", + "sha256:379d4c7abad5afbe9d88cc31ea8ca262296480a86af945b08214eb1a556a3e4d", + "sha256:3cacfaefe6089d477264001f90f55b7881ba615953414999c46cc9713ff93c8c", + "sha256:3e3424c554391dc9ef4a92ad28665756566a28fecf47308f91841f6c49288e66", + "sha256:46342fed0fff72efcda77040b14728049200cbba1279e0bf1188f1f2078c1d70", + "sha256:536d609c6963c50055bab766d9951b6c394759190d03311f3e9fcf194ca909e1", + "sha256:5d6850e6e36e332d5511a48a251790ddc545e16e8beaf046c03985c69ccb2676", + "sha256:6008adeca04a445ea6ef31b2cbaf1d01d02986047606f7da266629afee982630", + "sha256:64e723ca82a84053dd7bfcc986bdb34af8d9da83c521c19d6b472bc6880e191a", + "sha256:6b00e21f86598b6330f0019b40fb397e705135040dbedc2ca9a93c7441178e74", + "sha256:6d224f0c4c9c98290a6990259073f496fcec1b5cc613eecbd22786d398ded3ad", + "sha256:6dceb61d40cbfcf45f51e59933c784a50846dc03211054bd76b421a713dcdf19", + "sha256:7ac8f8eb153724f84885a1374999b7e45734bf93a87d8df1e7ce2146860edef6", + "sha256:85ccc5fa54c2ed64bd91ed3b4a627b9cce04646a659512a051fa82a92c04a448", + "sha256:869b5046d41abfea3e381dd143407b0d29b8282a904a19cb908fa24d090cc018", + "sha256:8bdb0285a0202888d19ec6b6d23d5990410decb932b709f2b0dfe216d031d218", + "sha256:8dfc5e195bbef80aabd81596ef52a1277ee7143fe419efc3c4d8ba2754671756", + "sha256:8e738a492b6221f8dcf281b67129510835461132b03024830ac0e554311a5c54", + "sha256:918440dea04521f499721c039863ef95433314b1db00ff826a02580c1f503e45", + "sha256:9641e21670c68c7e57d2053ddf6c443e4f0a6e18e547e86af3fad0795414a628", + "sha256:9d2f9d4cc2a53b38cabc2d6d80f7f9b7e3da26b2f53d48f05876fef7956b6968", + "sha256:a07f61fc452c43cd5328b392e52555f7d1952400a1ad09086c4a8addccbd138d", + "sha256:a3277f5fa7483c927fe3a7b017b39351610265308f5267ac6d4c2b64cc1d8d25", + "sha256:a4a3907011d39dbc3e37bdc5df0a8c93853c369039b59efa33a7b6669de04c60", + "sha256:aeb2c2688ed93b027eb0d26aa188ada34acb22dceea256d76390eea135083950", + "sha256:b094116f0b6155e36a304ff912f89bbb5067157aff5f94060ff20bbabdc8da06", + "sha256:b8ffb498a83d7e0305968289441914154fb0ef5d8b3157df02a90c6695978295", + "sha256:b9bb62fac84d5f2ff523304e59e5c439955fb3b7f44e3d7b2085184db74d733b", + "sha256:c61f66d93d712f6e03369b6a7769233bfda880b12f417eefdd4f16d1deb2fc4c", + "sha256:ca6e61dc52f601d1d224526360cdeab0d0712ec104a2ce6cc5ccef6ed9a233bc", + "sha256:ca7b26a5e456a843b9b6683eada193fc1f65c761b3a473941efe5a291f604c74", + "sha256:d12c923757de24e4e2110cf8832d83a886a4cf215c6e61ed506006872b43a6d1", + "sha256:d17bbc946f52ca67adf72a5ee783cd7cd3477f8f8796f59b4974a9b59cacc9ee", + "sha256:dfd1e1b9f0898817babf840b77ce9fe655ecbe8b1b327983df485b30df8cc011", + "sha256:e0860a348bf7004c812c8368d1fc7f77fe8e4c095d661a579196a9533778e156", + "sha256:f2f5968608b1fe2a1d00d01ad1017ee27efd99b3437e08b83ded9b7af3f6f766", + "sha256:f3771b23bb3675a06f5d885c3630b1d01ea6cac9e84a01aaf5508706dba546c5", + "sha256:f68ef3660677e6624c8cace943e4765545f8191313a07288a53d3da188bd8581", + "sha256:f86f368e1c7ce897bf2457b9eb61169a44e2ef797099fb5728482b8d69f3f016", + "sha256:f90515974b39f4dea2f27c0959688621b46d96d5a626cf9c53dbc653a895c05c", + "sha256:fe558371c1bdf3b8fa03e097c523fb9645b8730399c14fe7721ee9c9e2a545d3" ], "markers": "python_version >= '3.8'", - "version": "==7.4.0" + "version": "==7.4.1" }, "cryptography": { "hashes": [ - "sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960", - "sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a", - "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc", - "sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a", - "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf", - "sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1", - "sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39", - "sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406", - "sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a", - "sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a", - "sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c", - "sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be", - "sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15", - "sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2", - "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d", - "sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157", - "sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003", - "sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248", - "sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a", - "sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec", - "sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309", - "sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7", - "sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d" + "sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380", + "sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589", + "sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea", + "sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65", + "sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a", + "sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3", + "sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008", + "sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1", + "sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2", + "sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635", + "sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2", + "sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90", + "sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee", + "sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a", + "sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242", + "sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12", + "sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2", + "sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d", + "sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be", + "sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee", + "sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6", + "sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529", + "sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929", + "sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1", + "sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6", + "sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a", + "sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446", + "sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9", + "sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888", + "sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4", + "sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33", + "sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f" ], "markers": "python_version >= '3.7'", - "version": "==41.0.7" + "version": "==42.0.2" }, "daphne": { "hashes": [ @@ -2768,11 +2859,11 @@ }, "faker": { "hashes": [ - "sha256:3cd0e04ed7da1bb8037afc40d1127d19e0ac4afac247a1fe1d8dde9b5c6d6e5b", - "sha256:d1b8fe8e8fc96d816294a301741940c2229dcf1f5dd1231805666e4005cc6353" + "sha256:60e89e5c0b584e285a7db05eceba35011a241954afdab2853cb246c8a56700a2", + "sha256:b7f76bb1b2ac4cdc54442d955e36e477c387000f31ce46887fb9722a041be60b" ], "markers": "python_version >= '3.8'", - "version": "==22.1.0" + "version": "==23.1.0" }, "filelock": { "hashes": [ @@ -2889,69 +2980,69 @@ }, "markupsafe": { "hashes": [ - "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", - "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", - "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", - "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", - "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", - "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", - "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", - "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", - "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", - "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", - "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", - "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", - "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", - "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", - "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", - "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", - "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", - "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", - "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", - "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", - "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", - "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", - "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", - "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", - "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", - "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", - "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", - "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", - "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", - "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", - "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", - "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", - "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", - "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", - "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", - "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", - "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", - "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", - "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", - "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", - "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", - "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", - "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", - "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", - "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", - "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", - "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", - "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", - "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", - "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", - "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", - "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", - "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", - "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", - "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", - "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", - "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", - "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", - "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2", - "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11" + "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf", + "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff", + "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f", + "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3", + "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532", + "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f", + "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617", + "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df", + "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4", + "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906", + "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f", + "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4", + "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8", + "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371", + "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2", + "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465", + "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52", + "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6", + "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", + "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad", + "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2", + "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0", + "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029", + "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f", + "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a", + "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced", + "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5", + "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c", + "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf", + "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9", + "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", + "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", + "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3", + "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1", + "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46", + "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc", + "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a", + "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee", + "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900", + "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5", + "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea", + "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f", + "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5", + "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e", + "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a", + "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f", + "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50", + "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a", + "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", + "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4", + "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff", + "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2", + "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46", + "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b", + "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf", + "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5", + "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5", + "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab", + "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd", + "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68" ], "markers": "python_version >= '3.7'", - "version": "==2.1.3" + "version": "==2.1.5" }, "mergedeep": { "hashes": [ @@ -2971,20 +3062,20 @@ }, "mkdocs-glightbox": { "hashes": [ - "sha256:15310edfa1cf25cf483068932588f87ceb875148994431df1ee2c5aa03632db1", - "sha256:f46c675e20793d11c9fc29c9420dfb160ee1d8cfbf4782e82d006f953e19a14c" + "sha256:4e890140a97dd4ad128cb92174384bd0ac33adec3304bbd2b7c48d0847685c4f", + "sha256:9659631a9829d93d8fb0ce3a20a10261c258605ba4dc87a3b7b5d847b93a276d" ], "index": "pypi", - "version": "==0.3.6" + "version": "==0.3.7" }, "mkdocs-material": { "hashes": [ - "sha256:3d196ee67fad16b2df1a458d650a8ac1890294eaae368d26cee71bc24ad41c40", - "sha256:efd7cc8ae03296d728da9bd38f4db8b07ab61f9738a0cbd0dfaf2a15a50e7343" + "sha256:14563314bbf97da4bfafc69053772341babfaeb3329cde01d3e63cec03997af8", + "sha256:2a429213e83f84eda7a588e2b186316d806aac602b7f93990042f7a1f3d3cf65" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==9.5.4" + "version": "==9.5.8" }, "mkdocs-material-extensions": { "hashes": [ @@ -3012,45 +3103,45 @@ }, "numpy": { "hashes": [ - "sha256:02f98011ba4ab17f46f80f7f8f1c291ee7d855fcef0a5a98db80767a468c85cd", - "sha256:0b7e807d6888da0db6e7e75838444d62495e2b588b99e90dd80c3459594e857b", - "sha256:12c70ac274b32bc00c7f61b515126c9205323703abb99cd41836e8125ea0043e", - "sha256:1666f634cb3c80ccbd77ec97bc17337718f56d6658acf5d3b906ca03e90ce87f", - "sha256:18c3319a7d39b2c6a9e3bb75aab2304ab79a811ac0168a671a62e6346c29b03f", - "sha256:211ddd1e94817ed2d175b60b6374120244a4dd2287f4ece45d49228b4d529178", - "sha256:21a9484e75ad018974a2fdaa216524d64ed4212e418e0a551a2d83403b0531d3", - "sha256:39763aee6dfdd4878032361b30b2b12593fb445ddb66bbac802e2113eb8a6ac4", - "sha256:3c67423b3703f8fbd90f5adaa37f85b5794d3366948efe9a5190a5f3a83fc34e", - "sha256:46f47ee566d98849323f01b349d58f2557f02167ee301e5e28809a8c0e27a2d0", - "sha256:51c7f1b344f302067b02e0f5b5d2daa9ed4a721cf49f070280ac202738ea7f00", - "sha256:5f24750ef94d56ce6e33e4019a8a4d68cfdb1ef661a52cdaee628a56d2437419", - "sha256:697df43e2b6310ecc9d95f05d5ef20eacc09c7c4ecc9da3f235d39e71b7da1e4", - "sha256:6d45b3ec2faed4baca41c76617fcdcfa4f684ff7a151ce6fc78ad3b6e85af0a6", - "sha256:77810ef29e0fb1d289d225cabb9ee6cf4d11978a00bb99f7f8ec2132a84e0166", - "sha256:7ca4f24341df071877849eb2034948459ce3a07915c2734f1abb4018d9c49d7b", - "sha256:7f784e13e598e9594750b2ef6729bcd5a47f6cfe4a12cca13def35e06d8163e3", - "sha256:806dd64230dbbfaca8a27faa64e2f414bf1c6622ab78cc4264f7f5f028fee3bf", - "sha256:867e3644e208c8922a3be26fc6bbf112a035f50f0a86497f98f228c50c607bb2", - "sha256:8c66d6fec467e8c0f975818c1796d25c53521124b7cfb760114be0abad53a0a2", - "sha256:8ed07a90f5450d99dad60d3799f9c03c6566709bd53b497eb9ccad9a55867f36", - "sha256:9bc6d1a7f8cedd519c4b7b1156d98e051b726bf160715b769106661d567b3f03", - "sha256:9e1591f6ae98bcfac2a4bbf9221c0b92ab49762228f38287f6eeb5f3f55905ce", - "sha256:9e87562b91f68dd8b1c39149d0323b42e0082db7ddb8e934ab4c292094d575d6", - "sha256:a7081fd19a6d573e1a05e600c82a1c421011db7935ed0d5c483e9dd96b99cf13", - "sha256:a8474703bffc65ca15853d5fd4d06b18138ae90c17c8d12169968e998e448bb5", - "sha256:af36e0aa45e25c9f57bf684b1175e59ea05d9a7d3e8e87b7ae1a1da246f2767e", - "sha256:b1240f767f69d7c4c8a29adde2310b871153df9b26b5cb2b54a561ac85146485", - "sha256:b4d362e17bcb0011738c2d83e0a65ea8ce627057b2fdda37678f4374a382a137", - "sha256:b831295e5472954104ecb46cd98c08b98b49c69fdb7040483aff799a755a7374", - "sha256:b8c275f0ae90069496068c714387b4a0eba5d531aace269559ff2b43655edd58", - "sha256:bdd2b45bf079d9ad90377048e2747a0c82351989a2165821f0c96831b4a2a54b", - "sha256:cc0743f0302b94f397a4a65a660d4cd24267439eb16493fb3caad2e4389bccbb", - "sha256:da4b0c6c699a0ad73c810736303f7fbae483bcb012e38d7eb06a5e3b432c981b", - "sha256:f25e2811a9c932e43943a2615e65fc487a0b6b49218899e62e426e7f0a57eeda", - "sha256:f73497e8c38295aaa4741bdfa4fda1a5aedda5473074369eca10626835445511" + "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b", + "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818", + "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20", + "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0", + "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", + "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", + "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea", + "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c", + "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", + "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110", + "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be", + "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a", + "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", + "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5", + "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed", + "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd", + "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c", + "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", + "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0", + "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c", + "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a", + "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b", + "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", + "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6", + "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", + "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a", + "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30", + "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218", + "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", + "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", + "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2", + "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", + "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764", + "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", + "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3", + "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f" ], "markers": "python_version >= '3.9'", - "version": "==1.26.3" + "version": "==1.26.4" }, "packaging": { "hashes": [ @@ -3150,19 +3241,19 @@ }, "platformdirs": { "hashes": [ - "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380", - "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420" + "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068", + "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768" ], "markers": "python_version >= '3.8'", - "version": "==4.1.0" + "version": "==4.2.0" }, "pluggy": { "hashes": [ - "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12", - "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7" + "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981", + "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be" ], "markers": "python_version >= '3.8'", - "version": "==1.3.0" + "version": "==1.4.0" }, "pre-commit": { "hashes": [ @@ -3214,19 +3305,19 @@ }, "pyopenssl": { "hashes": [ - "sha256:6756834481d9ed5470f4a9393455154bc92fe7a64b7bc6ee2c804e78c52099b2", - "sha256:6b2cba5cc46e822750ec3e5a81ee12819850b11303630d575e98108a079c2b12" + "sha256:6aa33039a93fffa4563e655b61d11364d01264be8ccb49906101e02a334530bf", + "sha256:ba07553fb6fd6a7a2259adb9b84e12302a9a8a75c44046e8bb5d3e5ee887e3c3" ], - "version": "==23.3.0" + "version": "==24.0.0" }, "pytest": { "hashes": [ - "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280", - "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8" + "sha256:249b1b0864530ba251b7438274c4d251c58d868edaaec8762893ad4a0d71c36c", + "sha256:50fb9cbe836c3f20f0dfa99c565201fb75dc54c8d76373cd1bde06b06657bdb6" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==7.4.4" + "markers": "python_version >= '3.8'", + "version": "==8.0.0" }, "pytest-cov": { "hashes": [ @@ -3239,12 +3330,12 @@ }, "pytest-django": { "hashes": [ - "sha256:4e1c79d5261ade2dd58d91208017cd8f62cb4710b56e012ecd361d15d5d662a2", - "sha256:92d6fd46b1d79b54fb6b060bbb39428073396cec717d5f2e122a990d4b6aa5e8" + "sha256:5d054fe011c56f3b10f978f41a8efb2e5adfc7e680ef36fb571ada1f24779d90", + "sha256:ca1ddd1e0e4c227cf9e3e40a6afc6d106b3e70868fd2ac5798a22501271cd0c7" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.7.0" + "version": "==4.8.0" }, "pytest-env": { "hashes": [ @@ -3257,12 +3348,12 @@ }, "pytest-httpx": { "hashes": [ - "sha256:045774556a3633688742315a6981aab2806ce93bcbcc8444253ab87bca286800", - "sha256:a82505fdf59f19eaaf2853db3f3832b3dee35d3bc58000232db2b65c5fca0614" + "sha256:7d6fd29042e7b98ed98199ded120bc8100c8078ca306952666e89bf8807b95ff", + "sha256:ed08ed802e2b315b83cdd16f0b26cbb2b836c29e0fde5c18bc3105f1073e0332" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==0.28.0" + "version": "==0.29.0" }, "pytest-rerunfailures": { "hashes": [ @@ -3275,11 +3366,11 @@ }, "pytest-sugar": { "hashes": [ - "sha256:8cb5a4e5f8bbcd834622b0235db9e50432f4cbd71fef55b467fe44e43701e062", - "sha256:f1e74c1abfa55f7241cf7088032b6e378566f16b938f3f08905e2cf4494edd46" + "sha256:6422e83258f5b0c04ce7c632176c7732cab5fdb909cb39cca5c9139f81276c0a", + "sha256:70ebcd8fc5795dc457ff8b69d266a4e2e8a74ae0c3edc749381c64b5246c8dfd" ], "index": "pypi", - "version": "==0.9.7" + "version": "==1.0.0" }, "pytest-xdist": { "hashes": [ @@ -3295,6 +3386,7 @@ "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" ], + "index": "pypi", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.8.2" }, @@ -3360,6 +3452,7 @@ "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", + "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef", "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", @@ -3382,7 +3475,6 @@ "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" ], - "markers": "python_version >= '3.6'", "version": "==6.0.1" }, "pyyaml-env-tag": { @@ -3502,65 +3594,65 @@ }, "ruff": { "hashes": [ - "sha256:1c8eca1a47b4150dc0fbec7fe68fc91c695aed798532a18dbb1424e61e9b721f", - "sha256:2270504d629a0b064247983cbc495bed277f372fb9eaba41e5cf51f7ba705a6a", - "sha256:269302b31ade4cde6cf6f9dd58ea593773a37ed3f7b97e793c8594b262466b67", - "sha256:62ce2ae46303ee896fc6811f63d6dabf8d9c389da0f3e3f2bce8bc7f15ef5488", - "sha256:653230dd00aaf449eb5ff25d10a6e03bc3006813e2cb99799e568f55482e5cae", - "sha256:6b3dadc9522d0eccc060699a9816e8127b27addbb4697fc0c08611e4e6aeb8b5", - "sha256:7060156ecc572b8f984fd20fd8b0fcb692dd5d837b7606e968334ab7ff0090ab", - "sha256:722bafc299145575a63bbd6b5069cb643eaa62546a5b6398f82b3e4403329cab", - "sha256:80258bb3b8909b1700610dfabef7876423eed1bc930fe177c71c414921898efa", - "sha256:87b3acc6c4e6928459ba9eb7459dd4f0c4bf266a053c863d72a44c33246bfdbf", - "sha256:96f76536df9b26622755c12ed8680f159817be2f725c17ed9305b472a757cdbb", - "sha256:a53d8e35313d7b67eb3db15a66c08434809107659226a90dcd7acb2afa55faea", - "sha256:ab3f71f64498c7241123bb5a768544cf42821d2a537f894b22457a543d3ca7a9", - "sha256:ad3f8088b2dfd884820289a06ab718cde7d38b94972212cc4ba90d5fbc9955f3", - "sha256:b2027dde79d217b211d725fc833e8965dc90a16d0d3213f1298f97465956661b", - "sha256:bea9be712b8f5b4ebed40e1949379cfb2a7d907f42921cf9ab3aae07e6fba9eb", - "sha256:e3d241aa61f92b0805a7082bd89a9990826448e4d0398f0e2bc8f05c75c63d99" + "sha256:0034d5b6323e6e8fe91b2a1e55b02d92d0b582d2953a2b37a67a2d7dedbb7acc", + "sha256:00a818e2db63659570403e44383ab03c529c2b9678ba4ba6c105af7854008105", + "sha256:0a725823cb2a3f08ee743a534cb6935727d9e47409e4ad72c10a3faf042ad5ba", + "sha256:13471684694d41ae0f1e8e3a7497e14cd57ccb7dd72ae08d56a159d6c9c3e30e", + "sha256:3b42b5d8677cd0c72b99fcaf068ffc62abb5a19e71b4a3b9cfa50658a0af02f1", + "sha256:6b95ac9ce49b4fb390634d46d6ece32ace3acdd52814671ccaf20b7f60adb232", + "sha256:7022d66366d6fded4ba3889f73cd791c2d5621b2ccf34befc752cb0df70f5fad", + "sha256:a11567e20ea39d1f51aebd778685582d4c56ccb082c1161ffc10f79bebe6df35", + "sha256:be60592f9d218b52f03384d1325efa9d3b41e4c4d55ea022cd548547cc42cd2b", + "sha256:c92db7101ef5bfc18e96777ed7bc7c822d545fa5977e90a585accac43d22f18a", + "sha256:dc586724a95b7d980aa17f671e173df00f0a2eef23f8babbeee663229a938fec", + "sha256:dd81b911d28925e7e8b323e8d06951554655021df8dd4ac3045d7212ac4ba080", + "sha256:e3affdcbc2afb6f5bd0eb3130139ceedc5e3f28d206fe49f63073cb9e65988e0", + "sha256:e5cb5526d69bb9143c2e4d2a115d08ffca3d8e0fddc84925a7b54931c96f5c02", + "sha256:efababa8e12330aa94a53e90a81eb6e2d55f348bc2e71adbf17d9cad23c03ee6", + "sha256:f3ef052283da7dec1987bba8d8733051c2325654641dfe5877a4022108098683", + "sha256:fbd2288890b88e8aab4499e55148805b58ec711053588cc2f0196a44f6e3d855" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==0.1.14" + "version": "==0.2.1" }, "scipy": { "hashes": [ - "sha256:00150c5eae7b610c32589dda259eacc7c4f1665aedf25d921907f4d08a951b1c", - "sha256:028eccd22e654b3ea01ee63705681ee79933652b2d8f873e7949898dda6d11b6", - "sha256:1b7c3dca977f30a739e0409fb001056484661cb2541a01aba0bb0029f7b68db8", - "sha256:2c6ff6ef9cc27f9b3db93a6f8b38f97387e6e0591600369a297a50a8e96e835d", - "sha256:36750b7733d960d7994888f0d148d31ea3017ac15eef664194b4ef68d36a4a97", - "sha256:530f9ad26440e85766509dbf78edcfe13ffd0ab7fec2560ee5c36ff74d6269ff", - "sha256:5e347b14fe01003d3b78e196e84bd3f48ffe4c8a7b8a1afbcb8f5505cb710993", - "sha256:6550466fbeec7453d7465e74d4f4b19f905642c89a7525571ee91dd7adabb5a3", - "sha256:6df1468153a31cf55ed5ed39647279beb9cfb5d3f84369453b49e4b8502394fd", - "sha256:6e619aba2df228a9b34718efb023966da781e89dd3d21637b27f2e54db0410d7", - "sha256:8fce70f39076a5aa62e92e69a7f62349f9574d8405c0a5de6ed3ef72de07f446", - "sha256:90a2b78e7f5733b9de748f589f09225013685f9b218275257f8a8168ededaeaa", - "sha256:91af76a68eeae0064887a48e25c4e616fa519fa0d38602eda7e0f97d65d57937", - "sha256:933baf588daa8dc9a92c20a0be32f56d43faf3d1a60ab11b3f08c356430f6e56", - "sha256:acf8ed278cc03f5aff035e69cb511741e0418681d25fbbb86ca65429c4f4d9cd", - "sha256:ad669df80528aeca5f557712102538f4f37e503f0c5b9541655016dd0932ca79", - "sha256:b030c6674b9230d37c5c60ab456e2cf12f6784596d15ce8da9365e70896effc4", - "sha256:b9999c008ccf00e8fbcce1236f85ade5c569d13144f77a1946bef8863e8f6eb4", - "sha256:bc9a714581f561af0848e6b69947fda0614915f072dfd14142ed1bfe1b806710", - "sha256:ce7fff2e23ab2cc81ff452a9444c215c28e6305f396b2ba88343a567feec9660", - "sha256:cf00bd2b1b0211888d4dc75656c0412213a8b25e80d73898083f402b50f47e41", - "sha256:d10e45a6c50211fe256da61a11c34927c68f277e03138777bdebedd933712fea", - "sha256:ee410e6de8f88fd5cf6eadd73c135020bfbbbdfcd0f6162c36a7638a1ea8cc65", - "sha256:f313b39a7e94f296025e3cffc2c567618174c0b1dde173960cf23808f9fae4be", - "sha256:f3cd9e7b3c2c1ec26364856f9fbe78695fe631150f94cd1c22228456404cf1ec" + "sha256:196ebad3a4882081f62a5bf4aeb7326aa34b110e533aab23e4374fcccb0890dc", + "sha256:408c68423f9de16cb9e602528be4ce0d6312b05001f3de61fe9ec8b1263cad08", + "sha256:4bf5abab8a36d20193c698b0f1fc282c1d083c94723902c447e5d2f1780936a3", + "sha256:4c1020cad92772bf44b8e4cdabc1df5d87376cb219742549ef69fc9fd86282dd", + "sha256:5adfad5dbf0163397beb4aca679187d24aec085343755fcdbdeb32b3679f254c", + "sha256:5e32847e08da8d895ce09d108a494d9eb78974cf6de23063f93306a3e419960c", + "sha256:6546dc2c11a9df6926afcbdd8a3edec28566e4e785b915e849348c6dd9f3f490", + "sha256:730badef9b827b368f351eacae2e82da414e13cf8bd5051b4bdfd720271a5371", + "sha256:75ea2a144096b5e39402e2ff53a36fecfd3b960d786b7efd3c180e29c39e53f2", + "sha256:78e4402e140879387187f7f25d91cc592b3501a2e51dfb320f48dfb73565f10b", + "sha256:8b8066bce124ee5531d12a74b617d9ac0ea59245246410e19bca549656d9a40a", + "sha256:8bee4993817e204d761dba10dbab0774ba5a8612e57e81319ea04d84945375ba", + "sha256:913d6e7956c3a671de3b05ccb66b11bc293f56bfdef040583a7221d9e22a2e35", + "sha256:95e5c750d55cf518c398a8240571b0e0782c2d5a703250872f36eaf737751338", + "sha256:9c39f92041f490422924dfdb782527a4abddf4707616e07b021de33467f917bc", + "sha256:a24024d45ce9a675c1fb8494e8e5244efea1c7a09c60beb1eeb80373d0fecc70", + "sha256:a7ebda398f86e56178c2fa94cad15bf457a218a54a35c2a7b4490b9f9cb2676c", + "sha256:b360f1b6b2f742781299514e99ff560d1fe9bd1bff2712894b52abe528d1fd1e", + "sha256:bba1b0c7256ad75401c73e4b3cf09d1f176e9bd4248f0d3112170fb2ec4db067", + "sha256:c3003652496f6e7c387b1cf63f4bb720951cfa18907e998ea551e6de51a04467", + "sha256:e53958531a7c695ff66c2e7bb7b79560ffdc562e2051644c5576c39ff8efb563", + "sha256:e646d8571804a304e1da01040d21577685ce8e2db08ac58e543eaca063453e1c", + "sha256:e7e76cc48638228212c747ada851ef355c2bb5e7f939e10952bc504c11f4e372", + "sha256:f5f00ebaf8de24d14b8449981a2842d404152774c1a1d880c901bf454cb8e2a1", + "sha256:f7ce148dffcd64ade37b2df9315541f9adad6efcaa86866ee7dd5db0c8f041c3" ], "markers": "python_version >= '3.9'", - "version": "==1.11.4" + "version": "==1.12.0" }, "service-identity": { "hashes": [ - "sha256:87415a691d52fcad954a500cb81f424d0273f8e7e3ee7d766128f4575080f383", - "sha256:ecb33cd96307755041e978ab14f8b14e13b40f1fbd525a4dc78f46d2b986431d" + "sha256:6829c9d62fb832c2e1c435629b0a8c476e1929881f28bee4d20bc24161009221", + "sha256:a28caf8130c8a5c1c7a6f5293faaf239bbfb7751e4862436920ee6f2616f568a" ], - "version": "==23.1.0" + "version": "==24.1.0" }, "setuptools": { "hashes": [ @@ -3631,11 +3723,11 @@ }, "urllib3": { "hashes": [ - "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3", - "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54" + "sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20", + "sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224" ], "markers": "python_version >= '3.8'", - "version": "==2.1.0" + "version": "==2.2.0" }, "virtualenv": { "hashes": [ @@ -3675,6 +3767,7 @@ "sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44", "sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33" ], + "index": "pypi", "markers": "python_version >= '3.7'", "version": "==3.0.0" }, @@ -3740,20 +3833,20 @@ }, "celery-types": { "hashes": [ - "sha256:4048d7c59d2ce26127d32c2799b776d1b23a3de699eb6e6e9df1b8136dfe950f", - "sha256:93c0d25f2779bf5c985dabc4c2985d7eac8be43d1b2c05668e73973d6714f560" + "sha256:0ecad2fa5a6eded0a1f919e5e1e381cc2ff0635fe4b21db53b4661b6876d5b30", + "sha256:79a66637d1d6af5992d1dc80259d9538869941325e966006f1e795220519b9ac" ], "index": "pypi", - "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==0.18.0" + "markers": "python_version >= '3.9' and python_version < '4.0'", + "version": "==0.22.0" }, "certifi": { "hashes": [ - "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", - "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474" + "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", + "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1" ], "markers": "python_version >= '3.6'", - "version": "==2023.11.17" + "version": "==2024.2.2" }, "cffi": { "hashes": [ @@ -3810,7 +3903,7 @@ "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" ], - "markers": "python_version >= '3.8'", + "markers": "platform_python_implementation != 'PyPy'", "version": "==1.16.0" }, "charset-normalizer": { @@ -3911,41 +4004,50 @@ }, "cryptography": { "hashes": [ - "sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960", - "sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a", - "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc", - "sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a", - "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf", - "sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1", - "sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39", - "sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406", - "sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a", - "sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a", - "sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c", - "sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be", - "sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15", - "sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2", - "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d", - "sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157", - "sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003", - "sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248", - "sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a", - "sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec", - "sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309", - "sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7", - "sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d" + "sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380", + "sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589", + "sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea", + "sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65", + "sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a", + "sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3", + "sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008", + "sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1", + "sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2", + "sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635", + "sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2", + "sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90", + "sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee", + "sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a", + "sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242", + "sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12", + "sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2", + "sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d", + "sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be", + "sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee", + "sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6", + "sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529", + "sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929", + "sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1", + "sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6", + "sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a", + "sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446", + "sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9", + "sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888", + "sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4", + "sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33", + "sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f" ], "markers": "python_version >= '3.7'", - "version": "==41.0.7" + "version": "==42.0.2" }, "django": { "hashes": [ - "sha256:12498cc3cb8bc8038539fef9e90e95f507502436c1f0c3a673411324fa675d14", - "sha256:2cc2fc7d1708ada170ddd6c99f35cc25db664f165d3794bc7723f46b2f8c8984" + "sha256:a2d4c4d4ea0b6f0895acde632071aff6400bfc331228fc978b05452a0ff3e9f1", + "sha256:b1260ed381b10a11753c73444408e19869f3241fc45c985cd55a30177c789d13" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.2.9" + "version": "==4.2.10" }, "django-filter-stubs": { "hashes": [ @@ -4077,12 +4179,12 @@ }, "types-colorama": { "hashes": [ - "sha256:18294bc18f60dc0b4895de8119964a5d895f5e180c2d1308fdd33009c0fa0f38", - "sha256:49096b4c4cbfcaa11699a0470c36e4f5631f193fb980188e013ea64445d35656" + "sha256:3ab26dcd76d2f13b1b795ed5c87a1a1a29331ea64cf614bb6ae958a3cebc3a53", + "sha256:7ae4f58d407d387f4f98b24d81e1b7657ec754ea1dc4619ae5bd27f0c367637e" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==0.4.15.20240106" + "version": "==0.4.15.20240205" }, "types-dateparser": { "hashes": [ @@ -4095,52 +4197,55 @@ }, "types-docutils": { "hashes": [ - "sha256:03992ec976fbe080db588e1e56a83c5e4aba5c733022b25bb26cb84397b96049", - "sha256:d408f9305761905b157ea3cb80f53d4026bce7d4cc47312939118715467d0278" + "sha256:79d3bcef235f7c81a63f4f3dcf1d0b138985079bb32d02f5a7d266e1f9f361ba", + "sha256:ba4bfd4ff6dd19640ba7ab5d93900393a65897880f3650997964a943f4e79a6b" ], "markers": "python_version >= '3.8'", - "version": "==0.20.0.20240106" + "version": "==0.20.0.20240201" }, "types-markdown": { "hashes": [ - "sha256:be47d35cbe61d458bd17aec127f1da233cd6ed96fa9a131c710378a4e8857030", - "sha256:c23569d33718475dfae25c0036c6e6866f409e7077ee8a0728ab3db263d8e4a5" - ], - "markers": "python_version >= '3.8'", - "version": "==3.5.0.20240106" - }, - "types-pillow": { - "hashes": [ - "sha256:c04c68ace4a92af2e48904754750f1fcf2b1bc2ac7a75510cff11c3d7123dc92", - "sha256:d2c2ed7ece6b0becb4daf150e2374c8f4d977fe6efec625ef952e262576190e7" - ], - "markers": "python_version >= '3.8'", - "version": "==10.1.0.20240106" - }, - "types-psycopg2": { - "hashes": [ - "sha256:0d0a350449714ba28448c4f10a0a3aec36e9e3efd1450730e227e17b704a4bea", - "sha256:c20cf8236757f8ca4519068548f0c6c159158c9262cc7264c3f2f67f1f511b61" + "sha256:9acd36fef264d9ed5a96345c45f7d80f0d967059e92213998b3046fbb64f67fc", + "sha256:d6861d9d68e8268a5346d8a43d14727e6c636ebc6d49f2b8fc034c25996d35dd" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==2.9.21.20240106" + "version": "==3.5.0.20240129" + }, + "types-pillow": { + "hashes": [ + "sha256:abc339ae28af5916146a7729261480d68ac902cd4ff57e0bdd402eee7962644d", + "sha256:f0de5107ff8362ffdbbd53ec896202ac905e6ab22ae784b46bcdad160ea143b9" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==10.2.0.20240206" + }, + "types-psycopg2": { + "hashes": [ + "sha256:75a92735f62ba36397409ae4758f0241cbc4a9bb30f1f95d3b4a660fea7e7711", + "sha256:b05cf5d7ce0bd460319f6399fb358307e5af109bfcb6ed7b3d63c9ebec6b9be6" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==2.9.21.20240201" }, "types-pygments": { "hashes": [ "sha256:8052c574b0ab8f2dc94bdc4a31b9d48e8aa5a0f12398ef40cadadbe551da949b", "sha256:92e62ac37793e567cd2b0f64f1456c24fccce4041d9c5f869697a6739fde4fce" ], + "index": "pypi", "markers": "python_version >= '3.8'", "version": "==2.17.0.20240106" }, "types-pyopenssl": { "hashes": [ - "sha256:3d6f3462bec0c260caadf93fbb377225c126661b779c7d9ab99b6dad5ca10db9", - "sha256:47a7eedbd18b7bcad17efebf1c53416148f5a173918a6d75027e75e32fe039ae" + "sha256:24a255458b5b8a7fca8139cf56f2a8ad5a4f1a5f711b73a5bb9cb50dc688fab5", + "sha256:c812e5c1c35249f75ef5935708b2a997d62abf9745be222e5f94b9595472ab25" ], "markers": "python_version >= '3.8'", - "version": "==23.3.0.20240106" + "version": "==24.0.0.20240130" }, "types-python-dateutil": { "hashes": [ @@ -4153,10 +4258,11 @@ }, "types-pytz": { "hashes": [ - "sha256:1999a123a3dc0e39a2ef6d19f3f8584211de9e6a77fe7a0259f04a524e90a5cf", - "sha256:cc23d0192cd49c8f6bba44ee0c81e4586a8f30204970fc0894d209a6b08dab9a" + "sha256:9679eef0365db3af91ef7722c199dbb75ee5c1b67e3c4dd7bfbeb1b8a71c21a3", + "sha256:c93751ee20dfc6e054a0148f8f5227b9a00b79c90a4d3c9f464711a73179c89e" ], - "version": "==2023.3.1.1" + "markers": "python_version >= '3.8'", + "version": "==2024.1.0.20240203" }, "types-pyyaml": { "hashes": [ @@ -4176,20 +4282,20 @@ }, "types-requests": { "hashes": [ - "sha256:0e1c731c17f33618ec58e022b614a1a2ecc25f7dc86800b36ef341380402c612", - "sha256:da997b3b6a72cc08d09f4dba9802fdbabc89104b35fe24ee588e674037689354" + "sha256:03a28ce1d7cd54199148e043b2079cdded22d6795d19a2c2a6791a4b2b5e2eb5", + "sha256:9592a9a4cb92d6d75d9b491a41477272b710e021011a2a3061157e2fb1f1a5d1" ], "markers": "python_version >= '3.8'", - "version": "==2.31.0.20240106" + "version": "==2.31.0.20240125" }, "types-setuptools": { "hashes": [ - "sha256:b1da8981425723a674fd459c43dfa4402abeaee3f9cf682723ee9cf226125cc3", - "sha256:e077f9089578df3c9938f6e4aa1633f182ba6740a6fdb1333f162bae5dfcbadc" + "sha256:00835f959ff24ebc32c55da8df9d46e8df25e3c4bfacb43e98b61fde51a4bc41", + "sha256:22ad498cb585b22ce8c97ada1fccdf294a2e0dd7dc984a28535a84ea82f45b3f" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==69.0.0.20240106" + "version": "==69.0.0.20240125" }, "types-tqdm": { "hashes": [ @@ -4210,11 +4316,11 @@ }, "urllib3": { "hashes": [ - "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3", - "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54" + "sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20", + "sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224" ], "markers": "python_version >= '3.8'", - "version": "==2.1.0" + "version": "==2.2.0" } } } diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 2a2fc46a7..e712d4b59 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -82,13 +82,14 @@ class MatchingModelSerializer(serializers.ModelSerializer): def validate(self, data): # TODO: remove pending https://github.com/encode/django-rest-framework/issues/7173 - name = data["name"] if "name" in data else self.instance.name + name = data.get( + "name", + self.instance.name if hasattr(self.instance, "name") else None, + ) owner = ( data["owner"] if "owner" in data - else self.user - if hasattr(self, "user") - else None + else self.user if hasattr(self, "user") else None ) pk = self.instance.pk if hasattr(self.instance, "pk") else None if ("name" in data or "owner" in data) and self.Meta.model.objects.filter( @@ -261,7 +262,7 @@ class OwnedObjectSerializer(serializers.ModelSerializer, SetPermissionsMixin): if "set_permissions" in validated_data: self._set_permissions(validated_data["set_permissions"], instance) if "owner" in validated_data and "name" in self.Meta.fields: - name = validated_data["name"] if "name" in validated_data else instance.name + name = validated_data.get("name", instance.name) not_unique = ( self.Meta.model.objects.exclude(pk=instance.pk) .filter(owner=validated_data["owner"], name=name) @@ -443,7 +444,10 @@ class CustomFieldSerializer(serializers.ModelSerializer): def validate(self, attrs): # TODO: remove pending https://github.com/encode/django-rest-framework/issues/7173 - name = attrs["name"] if "name" in attrs else self.instance.name + name = attrs.get( + "name", + self.instance.name if hasattr(self.instance, "name") else None, + ) if ("name" in attrs) and self.Meta.model.objects.filter( name=name, ).exists(): @@ -697,10 +701,7 @@ class DocumentSerializer( custom_field_instance.field, doc_id, ) - if ( - "remove_inbox_tags" in validated_data - and validated_data["remove_inbox_tags"] - ): + if validated_data.get("remove_inbox_tags"): tag_ids_being_added = ( [ tag.id @@ -1352,7 +1353,7 @@ class BulkEditObjectPermissionsSerializer(serializers.Serializer, SetPermissions def validate(self, attrs): object_type = attrs["object_type"] objects = attrs["objects"] - permissions = attrs["permissions"] if "permissions" in attrs else None + permissions = attrs.get("permissions") self._validate_objects(objects, object_type) if permissions is not None: @@ -1514,7 +1515,7 @@ class WorkflowSerializer(serializers.ModelSerializer): for trigger in triggers: filter_has_tags = trigger.pop("filter_has_tags", None) trigger_instance, _ = WorkflowTrigger.objects.update_or_create( - id=trigger["id"] if "id" in trigger else None, + id=trigger.get("id"), defaults=trigger, ) if filter_has_tags is not None: @@ -1530,7 +1531,7 @@ class WorkflowSerializer(serializers.ModelSerializer): assign_change_groups = action.pop("assign_change_groups", None) assign_custom_fields = action.pop("assign_custom_fields", None) action_instance, _ = WorkflowAction.objects.update_or_create( - id=action["id"] if "id" in action else None, + id=action.get("id"), defaults=action, ) if assign_tags is not None: diff --git a/src/documents/signals/handlers.py b/src/documents/signals/handlers.py index c8657ce1d..9f2b9a222 100644 --- a/src/documents/signals/handlers.py +++ b/src/documents/signals/handlers.py @@ -559,15 +559,21 @@ def run_workflow( try: document.title = parse_doc_title_w_placeholders( action.assign_title, - document.correspondent.name - if document.correspondent is not None - else "", - document.document_type.name - if document.document_type is not None - else "", - document.owner.username - if document.owner is not None - else "", + ( + document.correspondent.name + if document.correspondent is not None + else "" + ), + ( + document.document_type.name + if document.document_type is not None + else "" + ), + ( + document.owner.username + if document.owner is not None + else "" + ), timezone.localtime(document.added), document.original_filename, timezone.localtime(document.created), diff --git a/src/documents/views.py b/src/documents/views.py index 0578cdb24..7a037c27d 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -175,16 +175,16 @@ class IndexView(TemplateView): context["full_name"] = self.request.user.get_full_name() context["styles_css"] = f"frontend/{self.get_frontend_language()}/styles.css" context["runtime_js"] = f"frontend/{self.get_frontend_language()}/runtime.js" - context[ - "polyfills_js" - ] = f"frontend/{self.get_frontend_language()}/polyfills.js" + context["polyfills_js"] = ( + f"frontend/{self.get_frontend_language()}/polyfills.js" + ) context["main_js"] = f"frontend/{self.get_frontend_language()}/main.js" - context[ - "webmanifest" - ] = f"frontend/{self.get_frontend_language()}/manifest.webmanifest" - context[ - "apple_touch_icon" - ] = f"frontend/{self.get_frontend_language()}/apple-touch-icon.png" + context["webmanifest"] = ( + f"frontend/{self.get_frontend_language()}/manifest.webmanifest" + ) + context["apple_touch_icon"] = ( + f"frontend/{self.get_frontend_language()}/apple-touch-icon.png" + ) return context @@ -722,9 +722,9 @@ class SearchResultSerializer(DocumentSerializer, PassUserMixin): r["__search_hit__"] = { "score": instance.score, "highlights": instance.highlights("content", text=doc.content), - "note_highlights": instance.highlights("notes", text=notes) - if doc - else None, + "note_highlights": ( + instance.highlights("notes", text=notes) if doc else None + ), "rank": instance.rank, } diff --git a/src/paperless/settings.py b/src/paperless/settings.py index d51ba9020..94400c8dd 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -1100,7 +1100,7 @@ def _get_nltk_language_setting(ocr_lang: str) -> Optional[str]: "tur": "turkish", } - return iso_code_to_nltk.get(ocr_lang, None) + return iso_code_to_nltk.get(ocr_lang) NLTK_ENABLED: Final[bool] = __get_boolean("PAPERLESS_ENABLE_NLTK", "yes") diff --git a/src/paperless/wsgi.py b/src/paperless/wsgi.py index 82e0744f7..6aab72299 100644 --- a/src/paperless/wsgi.py +++ b/src/paperless/wsgi.py @@ -6,6 +6,7 @@ It exposes the WSGI callable as a module-level variable named ``application``. For more information on this file, see https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/ """ + import os from django.core.wsgi import get_wsgi_application diff --git a/src/paperless_mail/admin.py b/src/paperless_mail/admin.py index be9df4c44..adec5e17c 100644 --- a/src/paperless_mail/admin.py +++ b/src/paperless_mail/admin.py @@ -9,11 +9,9 @@ from paperless_mail.models import ProcessedMail class MailAccountAdminForm(forms.ModelForm): - """Metadata classes used by Django admin to display the form.""" class Meta: - """Metadata class used by Django admin to display the form.""" model = MailAccount diff --git a/src/paperless_mail/mail.py b/src/paperless_mail/mail.py index 76e5ed2e7..6514014ca 100644 --- a/src/paperless_mail/mail.py +++ b/src/paperless_mail/mail.py @@ -739,9 +739,11 @@ class MailAccountHandler(LoggingMixin): correspondent_id=correspondent.id if correspondent else None, document_type_id=doc_type.id if doc_type else None, tag_ids=tag_ids, - owner_id=rule.owner.id - if (rule.assign_owner_from_rule and rule.owner) - else None, + owner_id=( + rule.owner.id + if (rule.assign_owner_from_rule and rule.owner) + else None + ), ) consume_task = consume_file.s( diff --git a/src/paperless_tesseract/parsers.py b/src/paperless_tesseract/parsers.py index ccddc987e..b6baa3289 100644 --- a/src/paperless_tesseract/parsers.py +++ b/src/paperless_tesseract/parsers.py @@ -205,9 +205,9 @@ class RasterisedDocumentParser(DocumentParser): } if "pdfa" in ocrmypdf_args["output_type"]: - ocrmypdf_args[ - "color_conversion_strategy" - ] = self.settings.color_conversion_strategy + ocrmypdf_args["color_conversion_strategy"] = ( + self.settings.color_conversion_strategy + ) if self.settings.mode == ModeChoices.FORCE or safe_fallback: ocrmypdf_args["force_ocr"] = True From b643a68fa38154cbd69e04184a84fb08a0e4e235 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 8 Feb 2024 10:13:15 -0800 Subject: [PATCH 35/41] Enhancement: bulk delete objects (#5688) --- docs/api.md | 9 +- src-ui/messages.xlf | 335 ++++++++++-------- .../management-list.component.html | 5 +- .../management-list.component.spec.ts | 42 ++- .../management-list.component.ts | 41 ++- .../rest/abstract-name-filter-service.spec.ts | 34 +- .../rest/abstract-name-filter-service.ts | 26 +- src-ui/src/environments/environment.prod.ts | 2 +- src-ui/src/environments/environment.ts | 2 +- src/documents/serialisers.py | 21 +- src/documents/tests/test_api_objects.py | 115 ++++++ src/documents/tests/test_api_permissions.py | 46 ++- src/documents/views.py | 68 ++-- src/paperless/settings.py | 2 +- src/paperless/urls.py | 8 +- 15 files changed, 535 insertions(+), 221 deletions(-) diff --git a/docs/api.md b/docs/api.md index bd5154ada..fa32bf2aa 100644 --- a/docs/api.md +++ b/docs/api.md @@ -375,14 +375,15 @@ The following methods are supported: ### Objects -Bulk editing for objects (tags, document types etc.) currently supports only updating permissions, using -the endpoint: `/api/bulk_edit_object_perms/` which requires a json payload of the format: +Bulk editing for objects (tags, document types etc.) currently supports set permissions or delete +operations, using the endpoint: `/api/bulk_edit_objects/`, which requires a json payload of the format: ```json { "objects": [LIST_OF_OBJECT_IDS], - "object_type": "tags", "correspondents", "document_types" or "storage_paths" - "owner": OWNER_ID // optional + "object_type": "tags", "correspondents", "document_types" or "storage_paths", + "operation": "set_permissions" or "delete", + "owner": OWNER_ID, // optional "permissions": { "view": { "users": [] ... }, "change": { ... } }, // (see 'set_permissions' format above) "merge": true / false // defaults to false, see above } diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index f2b356d9a..a959d9fb2 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -644,19 +644,19 @@ src/app/components/manage/management-list/management-list.component.html - 48 + 51 src/app/components/manage/management-list/management-list.component.html - 48 + 51 src/app/components/manage/management-list/management-list.component.html - 48 + 51 src/app/components/manage/management-list/management-list.component.html - 48 + 51
    @@ -966,7 +966,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.html - 78 + 82 src/app/components/document-list/filter-editor/filter-editor.component.html @@ -1262,35 +1262,35 @@ src/app/components/manage/management-list/management-list.component.html - 17 + 20 src/app/components/manage/management-list/management-list.component.html - 17 + 20 src/app/components/manage/management-list/management-list.component.html - 17 + 20 src/app/components/manage/management-list/management-list.component.html - 17 + 20 src/app/components/manage/management-list/management-list.component.html - 34 + 37 src/app/components/manage/management-list/management-list.component.html - 34 + 37 src/app/components/manage/management-list/management-list.component.html - 34 + 37 src/app/components/manage/management-list/management-list.component.html - 34 + 37 src/app/components/manage/workflows/workflows.component.html @@ -1354,7 +1354,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.html - 84 + 88 src/app/components/manage/custom-fields/custom-fields.component.html @@ -1370,19 +1370,19 @@ src/app/components/manage/management-list/management-list.component.html - 40 + 43 src/app/components/manage/management-list/management-list.component.html - 40 + 43 src/app/components/manage/management-list/management-list.component.html - 40 + 43 src/app/components/manage/management-list/management-list.component.html - 40 + 43 src/app/components/manage/workflows/workflows.component.html @@ -1429,7 +1429,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.html - 136 + 140 src/app/components/manage/custom-fields/custom-fields.component.html @@ -1445,39 +1445,55 @@ src/app/components/manage/management-list/management-list.component.html - 81 + 9 src/app/components/manage/management-list/management-list.component.html - 81 + 9 src/app/components/manage/management-list/management-list.component.html - 81 + 9 src/app/components/manage/management-list/management-list.component.html - 81 + 9 src/app/components/manage/management-list/management-list.component.html - 93 + 84 src/app/components/manage/management-list/management-list.component.html - 93 + 84 src/app/components/manage/management-list/management-list.component.html - 93 + 84 src/app/components/manage/management-list/management-list.component.html - 93 + 84 + + + src/app/components/manage/management-list/management-list.component.html + 96 + + + src/app/components/manage/management-list/management-list.component.html + 96 + + + src/app/components/manage/management-list/management-list.component.html + 96 + + + src/app/components/manage/management-list/management-list.component.html + 96 src/app/components/manage/management-list/management-list.component.ts - 205 + 208 src/app/components/manage/workflows/workflows.component.html @@ -1881,35 +1897,35 @@ src/app/components/manage/management-list/management-list.component.html - 80 + 83 src/app/components/manage/management-list/management-list.component.html - 80 + 83 src/app/components/manage/management-list/management-list.component.html - 80 + 83 src/app/components/manage/management-list/management-list.component.html - 80 + 83 src/app/components/manage/management-list/management-list.component.html - 90 + 93 src/app/components/manage/management-list/management-list.component.html - 90 + 93 src/app/components/manage/management-list/management-list.component.html - 90 + 93 src/app/components/manage/management-list/management-list.component.html - 90 + 93 src/app/components/manage/workflows/workflows.component.html @@ -1985,11 +2001,11 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 489 + 580 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 528 + 619 src/app/components/manage/custom-fields/custom-fields.component.ts @@ -2003,6 +2019,10 @@ src/app/components/manage/mail/mail.component.ts 173 + + src/app/components/manage/management-list/management-list.component.ts + 320 + src/app/components/manage/workflows/workflows.component.ts 97 @@ -2024,7 +2044,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 530 + 621 src/app/components/manage/custom-fields/custom-fields.component.ts @@ -2038,6 +2058,10 @@ src/app/components/manage/mail/mail.component.ts 175 + + src/app/components/manage/management-list/management-list.component.ts + 322 + src/app/components/manage/workflows/workflows.component.ts 99 @@ -2180,19 +2204,19 @@ src/app/components/manage/management-list/management-list.component.html - 87 + 90 src/app/components/manage/management-list/management-list.component.html - 87 + 90 src/app/components/manage/management-list/management-list.component.html - 87 + 90 src/app/components/manage/management-list/management-list.component.html - 87 + 90 @@ -2456,19 +2480,19 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 351 + 356 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 391 + 396 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 429 + 434 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 467 + 472 @@ -3713,18 +3737,45 @@ 27 + + Create + + src/app/components/common/filterable-dropdown/filterable-dropdown.component.html + 50 + + + src/app/components/common/share-links-dropdown/share-links-dropdown.component.html + 64 + + + src/app/components/manage/management-list/management-list.component.html + 12 + + + src/app/components/manage/management-list/management-list.component.html + 12 + + + src/app/components/manage/management-list/management-list.component.html + 12 + + + src/app/components/manage/management-list/management-list.component.html + 12 + + Apply src/app/components/common/filterable-dropdown/filterable-dropdown.component.html - 49 + 56 Click again to exclude items. src/app/components/common/filterable-dropdown/filterable-dropdown.component.html - 55 + 63 @@ -4248,29 +4299,6 @@ 51 - - Create - - src/app/components/common/share-links-dropdown/share-links-dropdown.component.html - 64 - - - src/app/components/manage/management-list/management-list.component.html - 9 - - - src/app/components/manage/management-list/management-list.component.html - 9 - - - src/app/components/manage/management-list/management-list.component.html - 9 - - - src/app/components/manage/management-list/management-list.component.html - 9 - - 1 day @@ -4423,7 +4451,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.html - 35 + 36 src/app/components/document-list/document-list.component.html @@ -4457,7 +4485,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.html - 102 + 106 src/app/components/document-list/document-card-large/document-card-large.component.html @@ -4590,7 +4618,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 296 + 301 this string is used to separate processing, failed and added on the file upload widget @@ -4683,7 +4711,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.html - 87 + 91 @@ -4744,7 +4772,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.html - 48 + 50 src/app/components/document-list/document-list.component.html @@ -4767,7 +4795,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.html - 61 + 64 src/app/components/document-list/document-list.component.html @@ -4994,7 +5022,11 @@ src/app/components/manage/management-list/management-list.component.ts - 201 + 204 + + + src/app/components/manage/management-list/management-list.component.ts + 318 @@ -5033,7 +5065,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 526 + 617 @@ -5093,7 +5125,7 @@ Filter correspondents src/app/components/document-list/bulk-editor/bulk-editor.component.html - 36 + 37 src/app/components/document-list/filter-editor/filter-editor.component.html @@ -5104,7 +5136,7 @@ Filter document types src/app/components/document-list/bulk-editor/bulk-editor.component.html - 49 + 51 src/app/components/document-list/filter-editor/filter-editor.component.html @@ -5115,7 +5147,7 @@ Filter storage paths src/app/components/document-list/bulk-editor/bulk-editor.component.html - 62 + 65 src/app/components/document-list/filter-editor/filter-editor.component.html @@ -5126,53 +5158,53 @@ Include: src/app/components/document-list/bulk-editor/bulk-editor.component.html - 108 + 112 Archived files src/app/components/document-list/bulk-editor/bulk-editor.component.html - 112,114 + 116,118 Original files src/app/components/document-list/bulk-editor/bulk-editor.component.html - 118,120 + 122,124 Use formatted filename src/app/components/document-list/bulk-editor/bulk-editor.component.html - 125,127 + 129,131 Error executing bulk operation src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 213 + 218 "" src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 288 + 293 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 294 + 299 "" and "" src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 290 + 295 This is for messages like 'modify "tag1" and "tag2"' @@ -5180,7 +5212,7 @@ and "" src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 298,300 + 303,305 this is for messages like 'modify "tag1", "tag2" and "tag3"' @@ -5188,14 +5220,14 @@ Confirm tags assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 315 + 320 This operation will add the tag "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 321 + 326 @@ -5204,14 +5236,14 @@ )"/> to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 326,328 + 331,333 This operation will remove the tag "" from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 334 + 339 @@ -5220,7 +5252,7 @@ )"/> from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 339,341 + 344,346 @@ -5231,98 +5263,98 @@ )"/> on selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 343,347 + 348,352 Confirm correspondent assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 384 + 389 This operation will assign the correspondent "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 386 + 391 This operation will remove the correspondent from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 388 + 393 Confirm document type assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 422 + 427 This operation will assign the document type "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 424 + 429 This operation will remove the document type from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 426 + 431 Confirm storage path assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 460 + 465 This operation will assign the storage path "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 462 + 467 This operation will remove the storage path from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 464 + 469 Delete confirm src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 487 + 578 This operation will permanently delete selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 488 + 579 Delete document(s) src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 491 + 582 This operation will permanently redo OCR for selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 527 + 618 @@ -6188,109 +6220,109 @@ src/app/components/manage/management-list/management-list.component.ts - 301 + 305 Filter by: src/app/components/manage/management-list/management-list.component.html - 16 + 19 src/app/components/manage/management-list/management-list.component.html - 16 + 19 src/app/components/manage/management-list/management-list.component.html - 16 + 19 src/app/components/manage/management-list/management-list.component.html - 16 + 19 Matching src/app/components/manage/management-list/management-list.component.html - 35 + 38 src/app/components/manage/management-list/management-list.component.html - 35 + 38 src/app/components/manage/management-list/management-list.component.html - 35 + 38 src/app/components/manage/management-list/management-list.component.html - 35 + 38 Document count src/app/components/manage/management-list/management-list.component.html - 36 + 39 src/app/components/manage/management-list/management-list.component.html - 36 + 39 src/app/components/manage/management-list/management-list.component.html - 36 + 39 src/app/components/manage/management-list/management-list.component.html - 36 + 39 Filter Documents src/app/components/manage/management-list/management-list.component.html - 79 + 82 src/app/components/manage/management-list/management-list.component.html - 79 + 82 src/app/components/manage/management-list/management-list.component.html - 79 + 82 src/app/components/manage/management-list/management-list.component.html - 79 + 82 {VAR_PLURAL, plural, =1 {One } other { total }} src/app/components/manage/management-list/management-list.component.html - 107 + 110 src/app/components/manage/management-list/management-list.component.html - 107 + 110 src/app/components/manage/management-list/management-list.component.html - 107 + 110 src/app/components/manage/management-list/management-list.component.html - 107 + 110 Automatic src/app/components/manage/management-list/management-list.component.ts - 113 + 116 src/app/data/matching-model.ts @@ -6301,7 +6333,7 @@ None src/app/components/manage/management-list/management-list.component.ts - 115 + 118 src/app/data/matching-model.ts @@ -6312,49 +6344,70 @@ Successfully created . src/app/components/manage/management-list/management-list.component.ts - 158 + 161 Error occurred while creating . src/app/components/manage/management-list/management-list.component.ts - 163 + 166 Successfully updated . src/app/components/manage/management-list/management-list.component.ts - 178 + 181 Error occurred while saving . src/app/components/manage/management-list/management-list.component.ts - 183 + 186 Associated documents will not be deleted. src/app/components/manage/management-list/management-list.component.ts - 203 + 206 Error while deleting element src/app/components/manage/management-list/management-list.component.ts - 219 + 222 Permissions updated successfully src/app/components/manage/management-list/management-list.component.ts - 294 + 298 + + + + This operation will permanently delete all objects. + + src/app/components/manage/management-list/management-list.component.ts + 319 + + + + Objects deleted successfully + + src/app/components/manage/management-list/management-list.component.ts + 333 + + + + Error deleting objects + + src/app/components/manage/management-list/management-list.component.ts + 339 diff --git a/src-ui/src/app/components/manage/management-list/management-list.component.html b/src-ui/src/app/components/manage/management-list/management-list.component.html index 9d6cf87c5..58101c388 100644 --- a/src-ui/src/app/components/manage/management-list/management-list.component.html +++ b/src-ui/src/app/components/manage/management-list/management-list.component.html @@ -2,9 +2,12 @@ - + diff --git a/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts b/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts index 6196a3c8a..710d3018a 100644 --- a/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts +++ b/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts @@ -37,6 +37,7 @@ import { MATCH_NONE } from 'src/app/data/matching-model' import { MATCH_LITERAL } from 'src/app/data/matching-model' import { PermissionsDialogComponent } from '../../common/permissions-dialog/permissions-dialog.component' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' +import { BulkEditObjectOperation } from 'src/app/services/rest/abstract-name-filter-service' const tags: Tag[] = [ { @@ -149,7 +150,7 @@ describe('ManagementListComponent', () => { const toastInfoSpy = jest.spyOn(toastService, 'showInfo') const reloadSpy = jest.spyOn(component, 'reloadData') - const createButton = fixture.debugElement.queryAll(By.css('button'))[2] + const createButton = fixture.debugElement.queryAll(By.css('button'))[3] createButton.triggerEventHandler('click') expect(modal).not.toBeUndefined() @@ -173,7 +174,7 @@ describe('ManagementListComponent', () => { const toastInfoSpy = jest.spyOn(toastService, 'showInfo') const reloadSpy = jest.spyOn(component, 'reloadData') - const editButton = fixture.debugElement.queryAll(By.css('button'))[6] + const editButton = fixture.debugElement.queryAll(By.css('button'))[7] editButton.triggerEventHandler('click') expect(modal).not.toBeUndefined() @@ -198,7 +199,7 @@ describe('ManagementListComponent', () => { const deleteSpy = jest.spyOn(tagService, 'delete') const reloadSpy = jest.spyOn(component, 'reloadData') - const deleteButton = fixture.debugElement.queryAll(By.css('button'))[7] + const deleteButton = fixture.debugElement.queryAll(By.css('button'))[8] deleteButton.triggerEventHandler('click') expect(modal).not.toBeUndefined() @@ -218,7 +219,7 @@ describe('ManagementListComponent', () => { it('should support quick filter for objects', () => { const qfSpy = jest.spyOn(documentListViewService, 'quickFilter') - const filterButton = fixture.debugElement.queryAll(By.css('button'))[5] + const filterButton = fixture.debugElement.queryAll(By.css('button'))[6] filterButton.triggerEventHandler('click') expect(qfSpy).toHaveBeenCalledWith([ { rule_type: FILTER_HAS_TAGS_ALL, value: tags[0].id.toString() }, @@ -246,7 +247,7 @@ describe('ManagementListComponent', () => { }) it('should support bulk edit permissions', () => { - const bulkEditPermsSpy = jest.spyOn(tagService, 'bulk_update_permissions') + const bulkEditPermsSpy = jest.spyOn(tagService, 'bulk_edit_objects') component.toggleSelected(tags[0]) component.toggleSelected(tags[1]) component.toggleSelected(tags[2]) @@ -280,4 +281,35 @@ describe('ManagementListComponent', () => { expect(bulkEditPermsSpy).toHaveBeenCalled() expect(successToastSpy).toHaveBeenCalled() }) + + it('should support bulk delete objects', () => { + const bulkEditSpy = jest.spyOn(tagService, 'bulk_edit_objects') + component.toggleSelected(tags[0]) + component.toggleSelected(tags[1]) + const selected = new Set([tags[0].id, tags[1].id]) + expect(component.selectedObjects).toEqual(selected) + let modal: NgbModalRef + modalService.activeInstances.subscribe((m) => (modal = m[m.length - 1])) + fixture.detectChanges() + component.delete() + expect(modal).not.toBeUndefined() + + // fail first + bulkEditSpy.mockReturnValueOnce( + throwError(() => new Error('error setting permissions')) + ) + const errorToastSpy = jest.spyOn(toastService, 'showError') + modal.componentInstance.confirmClicked.emit(null) + expect(bulkEditSpy).toHaveBeenCalledWith( + Array.from(selected), + BulkEditObjectOperation.Delete + ) + expect(errorToastSpy).toHaveBeenCalled() + + const successToastSpy = jest.spyOn(toastService, 'showInfo') + bulkEditSpy.mockReturnValueOnce(of('OK')) + modal.componentInstance.confirmClicked.emit(null) + expect(bulkEditSpy).toHaveBeenCalled() + expect(successToastSpy).toHaveBeenCalled() + }) }) diff --git a/src-ui/src/app/components/manage/management-list/management-list.component.ts b/src-ui/src/app/components/manage/management-list/management-list.component.ts index a89b5e4f6..0b0365f06 100644 --- a/src-ui/src/app/components/manage/management-list/management-list.component.ts +++ b/src-ui/src/app/components/manage/management-list/management-list.component.ts @@ -28,7 +28,10 @@ import { PermissionsService, PermissionType, } from 'src/app/services/permissions.service' -import { AbstractNameFilterService } from 'src/app/services/rest/abstract-name-filter-service' +import { + AbstractNameFilterService, + BulkEditObjectOperation, +} from 'src/app/services/rest/abstract-name-filter-service' import { ToastService } from 'src/app/services/toast.service' import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component' import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component' @@ -282,8 +285,9 @@ export abstract class ManagementListComponent ({ permissions, merge }) => { modal.componentInstance.buttonsEnabled = false this.service - .bulk_update_permissions( + .bulk_edit_objects( Array.from(this.selectedObjects), + BulkEditObjectOperation.SetPermissions, permissions, merge ) @@ -306,4 +310,37 @@ export abstract class ManagementListComponent } ) } + + delete() { + let modal = this.modalService.open(ConfirmDialogComponent, { + backdrop: 'static', + }) + modal.componentInstance.title = $localize`Confirm delete` + modal.componentInstance.messageBold = $localize`This operation will permanently delete all objects.` + modal.componentInstance.message = $localize`This operation cannot be undone.` + modal.componentInstance.btnClass = 'btn-danger' + modal.componentInstance.btnCaption = $localize`Proceed` + modal.componentInstance.confirmClicked.subscribe(() => { + modal.componentInstance.buttonsEnabled = false + this.service + .bulk_edit_objects( + Array.from(this.selectedObjects), + BulkEditObjectOperation.Delete + ) + .subscribe({ + next: () => { + modal.close() + this.toastService.showInfo($localize`Objects deleted successfully`) + this.reloadData() + }, + error: (error) => { + modal.componentInstance.buttonsEnabled = true + this.toastService.showError( + $localize`Error deleting objects`, + error + ) + }, + }) + }) + } } diff --git a/src-ui/src/app/services/rest/abstract-name-filter-service.spec.ts b/src-ui/src/app/services/rest/abstract-name-filter-service.spec.ts index e09270701..f61efc640 100644 --- a/src-ui/src/app/services/rest/abstract-name-filter-service.spec.ts +++ b/src-ui/src/app/services/rest/abstract-name-filter-service.spec.ts @@ -2,7 +2,10 @@ import { HttpTestingController } from '@angular/common/http/testing' import { Subscription } from 'rxjs' import { TestBed } from '@angular/core/testing' import { environment } from 'src/environments/environment' -import { AbstractNameFilterService } from './abstract-name-filter-service' +import { + AbstractNameFilterService, + BulkEditObjectOperation, +} from './abstract-name-filter-service' import { commonAbstractPaperlessServiceTests } from './abstract-paperless-service.spec' let httpTestingController: HttpTestingController @@ -53,8 +56,9 @@ export const commonAbstractNameFilterPaperlessServiceTests = ( }, } subscription = service - .bulk_update_permissions( + .bulk_edit_objects( [1, 2], + BulkEditObjectOperation.SetPermissions, { owner, set_permissions: permissions, @@ -63,9 +67,33 @@ export const commonAbstractNameFilterPaperlessServiceTests = ( ) .subscribe() const req = httpTestingController.expectOne( - `${environment.apiBaseUrl}bulk_edit_object_perms/` + `${environment.apiBaseUrl}bulk_edit_objects/` ) expect(req.request.method).toEqual('POST') + expect(req.request.body).toEqual({ + objects: [1, 2], + object_type: endpoint, + operation: BulkEditObjectOperation.SetPermissions, + permissions, + owner, + merge: true, + }) + req.flush([]) + }) + + test('should call appropriate api endpoint for bulk delete objects', () => { + subscription = service + .bulk_edit_objects([1, 2], BulkEditObjectOperation.Delete) + .subscribe() + const req = httpTestingController.expectOne( + `${environment.apiBaseUrl}bulk_edit_objects/` + ) + expect(req.request.method).toEqual('POST') + expect(req.request.body).toEqual({ + objects: [1, 2], + object_type: endpoint, + operation: BulkEditObjectOperation.Delete, + }) req.flush([]) }) }) diff --git a/src-ui/src/app/services/rest/abstract-name-filter-service.ts b/src-ui/src/app/services/rest/abstract-name-filter-service.ts index b38994086..1018f0fa2 100644 --- a/src-ui/src/app/services/rest/abstract-name-filter-service.ts +++ b/src-ui/src/app/services/rest/abstract-name-filter-service.ts @@ -3,6 +3,11 @@ import { AbstractPaperlessService } from './abstract-paperless-service' import { PermissionsObject } from 'src/app/data/object-with-permissions' import { Observable } from 'rxjs' +export enum BulkEditObjectOperation { + SetPermissions = 'set_permissions', + Delete = 'delete', +} + export abstract class AbstractNameFilterService< T extends ObjectWithId, > extends AbstractPaperlessService { @@ -24,17 +29,22 @@ export abstract class AbstractNameFilterService< return this.list(page, pageSize, sortField, sortReverse, params) } - bulk_update_permissions( + bulk_edit_objects( objects: Array, - permissions: { owner: number; set_permissions: PermissionsObject }, - merge: boolean + operation: BulkEditObjectOperation, + permissions: { owner: number; set_permissions: PermissionsObject } = null, + merge: boolean = null ): Observable { - return this.http.post(`${this.baseUrl}bulk_edit_object_perms/`, { + const params = { objects, object_type: this.resourceName, - owner: permissions.owner, - permissions: permissions.set_permissions, - merge, - }) + operation, + } + if (operation === BulkEditObjectOperation.SetPermissions) { + params['owner'] = permissions?.owner + params['permissions'] = permissions?.set_permissions + params['merge'] = merge + } + return this.http.post(`${this.baseUrl}bulk_edit_objects/`, params) } } diff --git a/src-ui/src/environments/environment.prod.ts b/src-ui/src/environments/environment.prod.ts index 6135915d9..7de16a988 100644 --- a/src-ui/src/environments/environment.prod.ts +++ b/src-ui/src/environments/environment.prod.ts @@ -3,7 +3,7 @@ const base_url = new URL(document.baseURI) export const environment = { production: true, apiBaseUrl: document.baseURI + 'api/', - apiVersion: '4', + apiVersion: '5', appTitle: 'Paperless-ngx', version: '2.4.3-dev', webSocketHost: window.location.host, diff --git a/src-ui/src/environments/environment.ts b/src-ui/src/environments/environment.ts index fccb8927c..18715e90f 100644 --- a/src-ui/src/environments/environment.ts +++ b/src-ui/src/environments/environment.ts @@ -5,7 +5,7 @@ export const environment = { production: false, apiBaseUrl: 'http://localhost:8000/api/', - apiVersion: '4', + apiVersion: '5', appTitle: 'Paperless-ngx', version: 'DEVELOPMENT', webSocketHost: 'localhost:8000', diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index e712d4b59..5fa104640 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -1281,7 +1281,7 @@ class ShareLinkSerializer(OwnedObjectSerializer): return super().create(validated_data) -class BulkEditObjectPermissionsSerializer(serializers.Serializer, SetPermissionsMixin): +class BulkEditObjectsSerializer(serializers.Serializer, SetPermissionsMixin): objects = serializers.ListField( required=True, allow_empty=False, @@ -1301,6 +1301,16 @@ class BulkEditObjectPermissionsSerializer(serializers.Serializer, SetPermissions write_only=True, ) + operation = serializers.ChoiceField( + choices=[ + "set_permissions", + "delete", + ], + label="Operation", + required=True, + write_only=True, + ) + owner = serializers.PrimaryKeyRelatedField( queryset=User.objects.all(), required=False, @@ -1353,11 +1363,14 @@ class BulkEditObjectPermissionsSerializer(serializers.Serializer, SetPermissions def validate(self, attrs): object_type = attrs["object_type"] objects = attrs["objects"] - permissions = attrs.get("permissions") + operation = attrs.get("operation") self._validate_objects(objects, object_type) - if permissions is not None: - self._validate_permissions(permissions) + + if operation == "set_permissions": + permissions = attrs.get("permissions") + if permissions is not None: + self._validate_permissions(permissions) return attrs diff --git a/src/documents/tests/test_api_objects.py b/src/documents/tests/test_api_objects.py index e894cae90..3b38f2b5f 100644 --- a/src/documents/tests/test_api_objects.py +++ b/src/documents/tests/test_api_objects.py @@ -222,3 +222,118 @@ class TestApiStoragePaths(DirectoriesMixin, APITestCase): args, _ = bulk_update_mock.call_args self.assertCountEqual([document.pk], args[0]) + + +class TestBulkEditObjects(APITestCase): + # See test_api_permissions.py for bulk tests on permissions + def setUp(self): + super().setUp() + + self.temp_admin = User.objects.create_superuser(username="temp_admin") + self.client.force_authenticate(user=self.temp_admin) + + self.t1 = Tag.objects.create(name="t1") + self.t2 = Tag.objects.create(name="t2") + self.c1 = Correspondent.objects.create(name="c1") + self.dt1 = DocumentType.objects.create(name="dt1") + self.sp1 = StoragePath.objects.create(name="sp1") + self.user1 = User.objects.create(username="user1") + self.user2 = User.objects.create(username="user2") + self.user3 = User.objects.create(username="user3") + + def test_bulk_objects_delete(self): + """ + GIVEN: + - Existing objects + WHEN: + - bulk_edit_objects API endpoint is called with delete operation + THEN: + - Objects are deleted + """ + response = self.client.post( + "/api/bulk_edit_objects/", + json.dumps( + { + "objects": [self.t1.id, self.t2.id], + "object_type": "tags", + "operation": "delete", + }, + ), + content_type="application/json", + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(Tag.objects.count(), 0) + + response = self.client.post( + "/api/bulk_edit_objects/", + json.dumps( + { + "objects": [self.c1.id], + "object_type": "correspondents", + "operation": "delete", + }, + ), + content_type="application/json", + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(Correspondent.objects.count(), 0) + + response = self.client.post( + "/api/bulk_edit_objects/", + json.dumps( + { + "objects": [self.dt1.id], + "object_type": "document_types", + "operation": "delete", + }, + ), + content_type="application/json", + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(DocumentType.objects.count(), 0) + + response = self.client.post( + "/api/bulk_edit_objects/", + json.dumps( + { + "objects": [self.sp1.id], + "object_type": "storage_paths", + "operation": "delete", + }, + ), + content_type="application/json", + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(StoragePath.objects.count(), 0) + + def test_bulk_edit_object_permissions_insufficient_perms(self): + """ + GIVEN: + - Objects owned by user other than logged in user + WHEN: + - bulk_edit_objects API endpoint is called with delete operation + THEN: + - User is not able to delete objects + """ + self.t1.owner = User.objects.get(username="temp_admin") + self.t1.save() + self.client.force_authenticate(user=self.user1) + + response = self.client.post( + "/api/bulk_edit_objects/", + json.dumps( + { + "objects": [self.t1.id, self.t2.id], + "object_type": "tags", + "operation": "delete", + }, + ), + content_type="application/json", + ) + + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response.content, b"Insufficient permissions") diff --git a/src/documents/tests/test_api_permissions.py b/src/documents/tests/test_api_permissions.py index 851608f37..92e47a1ed 100644 --- a/src/documents/tests/test_api_permissions.py +++ b/src/documents/tests/test_api_permissions.py @@ -717,7 +717,7 @@ class TestBulkEditObjectPermissions(APITestCase): GIVEN: - Existing objects WHEN: - - bulk_edit_object_perms API endpoint is called + - bulk_edit_objects API endpoint is called with set_permissions operation THEN: - Permissions and / or owner are changed """ @@ -733,11 +733,12 @@ class TestBulkEditObjectPermissions(APITestCase): } response = self.client.post( - "/api/bulk_edit_object_perms/", + "/api/bulk_edit_objects/", json.dumps( { "objects": [self.t1.id, self.t2.id], "object_type": "tags", + "operation": "set_permissions", "permissions": permissions, }, ), @@ -748,11 +749,12 @@ class TestBulkEditObjectPermissions(APITestCase): self.assertIn(self.user1, get_users_with_perms(self.t1)) response = self.client.post( - "/api/bulk_edit_object_perms/", + "/api/bulk_edit_objects/", json.dumps( { "objects": [self.c1.id], "object_type": "correspondents", + "operation": "set_permissions", "permissions": permissions, }, ), @@ -763,11 +765,12 @@ class TestBulkEditObjectPermissions(APITestCase): self.assertIn(self.user1, get_users_with_perms(self.c1)) response = self.client.post( - "/api/bulk_edit_object_perms/", + "/api/bulk_edit_objects/", json.dumps( { "objects": [self.dt1.id], "object_type": "document_types", + "operation": "set_permissions", "permissions": permissions, }, ), @@ -778,11 +781,12 @@ class TestBulkEditObjectPermissions(APITestCase): self.assertIn(self.user1, get_users_with_perms(self.dt1)) response = self.client.post( - "/api/bulk_edit_object_perms/", + "/api/bulk_edit_objects/", json.dumps( { "objects": [self.sp1.id], "object_type": "storage_paths", + "operation": "set_permissions", "permissions": permissions, }, ), @@ -793,11 +797,12 @@ class TestBulkEditObjectPermissions(APITestCase): self.assertIn(self.user1, get_users_with_perms(self.sp1)) response = self.client.post( - "/api/bulk_edit_object_perms/", + "/api/bulk_edit_objects/", json.dumps( { "objects": [self.t1.id, self.t2.id], "object_type": "tags", + "operation": "set_permissions", "owner": self.user3.id, }, ), @@ -808,11 +813,12 @@ class TestBulkEditObjectPermissions(APITestCase): self.assertEqual(Tag.objects.get(pk=self.t2.id).owner, self.user3) response = self.client.post( - "/api/bulk_edit_object_perms/", + "/api/bulk_edit_objects/", json.dumps( { "objects": [self.sp1.id], "object_type": "storage_paths", + "operation": "set_permissions", "owner": self.user3.id, }, ), @@ -827,7 +833,7 @@ class TestBulkEditObjectPermissions(APITestCase): GIVEN: - Existing objects WHEN: - - bulk_edit_object_perms API endpoint is called with merge=True or merge=False (default) + - bulk_edit_objects API endpoint is called with set_permissions operation with merge=True or merge=False (default) THEN: - Permissions and / or owner are replaced or merged, depending on the merge flag """ @@ -848,13 +854,14 @@ class TestBulkEditObjectPermissions(APITestCase): # merge=True response = self.client.post( - "/api/bulk_edit_object_perms/", + "/api/bulk_edit_objects/", json.dumps( { "objects": [self.t1.id, self.t2.id], "object_type": "tags", "owner": self.user1.id, "permissions": permissions, + "operation": "set_permissions", "merge": True, }, ), @@ -877,12 +884,13 @@ class TestBulkEditObjectPermissions(APITestCase): # merge=False (default) response = self.client.post( - "/api/bulk_edit_object_perms/", + "/api/bulk_edit_objects/", json.dumps( { "objects": [self.t1.id, self.t2.id], "object_type": "tags", "permissions": permissions, + "operation": "set_permissions", "merge": False, }, ), @@ -900,7 +908,7 @@ class TestBulkEditObjectPermissions(APITestCase): GIVEN: - Objects owned by user other than logged in user WHEN: - - bulk_edit_object_perms API endpoint is called + - bulk_edit_objects API endpoint is called with set_permissions operation THEN: - User is not able to change permissions """ @@ -909,11 +917,12 @@ class TestBulkEditObjectPermissions(APITestCase): self.client.force_authenticate(user=self.user1) response = self.client.post( - "/api/bulk_edit_object_perms/", + "/api/bulk_edit_objects/", json.dumps( { "objects": [self.t1.id, self.t2.id], "object_type": "tags", + "operation": "set_permissions", "owner": self.user1.id, }, ), @@ -928,17 +937,18 @@ class TestBulkEditObjectPermissions(APITestCase): GIVEN: - Existing objects WHEN: - - bulk_edit_object_perms API endpoint is called with invalid params + - bulk_edit_objects API endpoint is called with set_permissions operation with invalid params THEN: - Validation fails """ # not a list response = self.client.post( - "/api/bulk_edit_object_perms/", + "/api/bulk_edit_objects/", json.dumps( { "objects": self.t1.id, "object_type": "tags", + "operation": "set_permissions", "owner": self.user1.id, }, ), @@ -949,7 +959,7 @@ class TestBulkEditObjectPermissions(APITestCase): # not a list of ints response = self.client.post( - "/api/bulk_edit_object_perms/", + "/api/bulk_edit_objects/", json.dumps( { "objects": ["one"], @@ -964,11 +974,12 @@ class TestBulkEditObjectPermissions(APITestCase): # duplicates response = self.client.post( - "/api/bulk_edit_object_perms/", + "/api/bulk_edit_objects/", json.dumps( { "objects": [self.t1.id, self.t2.id, self.t1.id], "object_type": "tags", + "operation": "set_permissions", "owner": self.user1.id, }, ), @@ -979,11 +990,12 @@ class TestBulkEditObjectPermissions(APITestCase): # not a valid object type response = self.client.post( - "/api/bulk_edit_object_perms/", + "/api/bulk_edit_objects/", json.dumps( { "objects": [1], "object_type": "madeup", + "operation": "set_permissions", "owner": self.user1.id, }, ), diff --git a/src/documents/views.py b/src/documents/views.py index 7a037c27d..3be7f4ec6 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -115,7 +115,7 @@ from documents.permissions import has_perms_owner_aware from documents.permissions import set_permissions_for_object from documents.serialisers import AcknowledgeTasksViewSerializer from documents.serialisers import BulkDownloadSerializer -from documents.serialisers import BulkEditObjectPermissionsSerializer +from documents.serialisers import BulkEditObjectsSerializer from documents.serialisers import BulkEditSerializer from documents.serialisers import CorrespondentSerializer from documents.serialisers import CustomFieldSerializer @@ -1401,9 +1401,9 @@ def serve_file(doc: Document, use_archive: bool, disposition: str): return response -class BulkEditObjectPermissionsView(GenericAPIView, PassUserMixin): +class BulkEditObjectsView(GenericAPIView, PassUserMixin): permission_classes = (IsAuthenticated,) - serializer_class = BulkEditObjectPermissionsSerializer + serializer_class = BulkEditObjectsSerializer parser_classes = (parsers.JSONParser,) def post(self, request, *args, **kwargs): @@ -1414,42 +1414,52 @@ class BulkEditObjectPermissionsView(GenericAPIView, PassUserMixin): object_type = serializer.validated_data.get("object_type") object_ids = serializer.validated_data.get("objects") object_class = serializer.get_object_class(object_type) - permissions = serializer.validated_data.get("permissions") - owner = serializer.validated_data.get("owner") - merge = serializer.validated_data.get("merge") + operation = serializer.validated_data.get("operation") + + objs = object_class.objects.filter(pk__in=object_ids) if not user.is_superuser: - objs = object_class.objects.filter(pk__in=object_ids) has_perms = all((obj.owner == user or obj.owner is None) for obj in objs) if not has_perms: return HttpResponseForbidden("Insufficient permissions") - try: - qs = object_class.objects.filter(id__in=object_ids) + if operation == "set_permissions": + permissions = serializer.validated_data.get("permissions") + owner = serializer.validated_data.get("owner") + merge = serializer.validated_data.get("merge") - # if merge is true, we dont want to remove the owner - if "owner" in serializer.validated_data and ( - not merge or (merge and owner is not None) - ): - # if merge is true, we dont want to overwrite the owner - qs_owner_update = qs.filter(owner__isnull=True) if merge else qs - qs_owner_update.update(owner=owner) + try: + qs = object_class.objects.filter(id__in=object_ids) - if "permissions" in serializer.validated_data: - for obj in qs: - set_permissions_for_object( - permissions=permissions, - object=obj, - merge=merge, - ) + # if merge is true, we dont want to remove the owner + if "owner" in serializer.validated_data and ( + not merge or (merge and owner is not None) + ): + # if merge is true, we dont want to overwrite the owner + qs_owner_update = qs.filter(owner__isnull=True) if merge else qs + qs_owner_update.update(owner=owner) - return Response({"result": "OK"}) - except Exception as e: - logger.warning(f"An error occurred performing bulk permissions edit: {e!s}") - return HttpResponseBadRequest( - "Error performing bulk permissions edit, check logs for more detail.", - ) + if "permissions" in serializer.validated_data: + for obj in qs: + set_permissions_for_object( + permissions=permissions, + object=obj, + merge=merge, + ) + + except Exception as e: + logger.warning( + f"An error occurred performing bulk permissions edit: {e!s}", + ) + return HttpResponseBadRequest( + "Error performing bulk permissions edit, check logs for more detail.", + ) + + elif operation == "delete": + objs.delete() + + return Response({"result": "OK"}) class WorkflowTriggerViewSet(ModelViewSet): diff --git a/src/paperless/settings.py b/src/paperless/settings.py index 94400c8dd..a9792db9f 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -322,7 +322,7 @@ REST_FRAMEWORK = { "DEFAULT_VERSION": "1", # Make sure these are ordered and that the most recent version appears # last - "ALLOWED_VERSIONS": ["1", "2", "3", "4"], + "ALLOWED_VERSIONS": ["1", "2", "3", "4", "5"], } if DEBUG: diff --git a/src/paperless/urls.py b/src/paperless/urls.py index 74f6fc108..0419b8e66 100644 --- a/src/paperless/urls.py +++ b/src/paperless/urls.py @@ -16,7 +16,7 @@ from rest_framework.routers import DefaultRouter from documents.views import AcknowledgeTasksView from documents.views import BulkDownloadView -from documents.views import BulkEditObjectPermissionsView +from documents.views import BulkEditObjectsView from documents.views import BulkEditView from documents.views import CorrespondentViewSet from documents.views import CustomFieldViewSet @@ -129,9 +129,9 @@ urlpatterns = [ ), path("token/", views.obtain_auth_token), re_path( - "^bulk_edit_object_perms/", - BulkEditObjectPermissionsView.as_view(), - name="bulk_edit_object_permissions", + "^bulk_edit_objects/", + BulkEditObjectsView.as_view(), + name="bulk_edit_objects", ), path("profile/generate_auth_token/", GenerateAuthTokenView.as_view()), path( From 6487dab132b373c2f7af321806236e3970a8def8 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 8 Feb 2024 10:23:06 -0800 Subject: [PATCH 36/41] Enhancement: confirm buttons (#5680) --- src-ui/messages.xlf | 325 ++++++++---------- src-ui/src/app/app.module.ts | 2 + .../admin/settings/settings.component.html | 10 +- .../admin/settings/settings.component.spec.ts | 2 + .../confirm-button.component.html | 22 ++ .../confirm-button.component.scss | 12 + .../confirm-button.component.spec.ts | 37 ++ .../confirm-button.component.ts | 55 +++ .../workflow-edit-dialog.component.html | 20 +- .../workflow-edit-dialog.component.spec.ts | 2 + .../profile-edit-dialog.component.html | 29 +- .../profile-edit-dialog.component.spec.ts | 2 + .../management-list.component.html | 62 ++-- .../management-list.component.spec.ts | 18 +- .../management-list.component.ts | 48 +-- 15 files changed, 368 insertions(+), 278 deletions(-) create mode 100644 src-ui/src/app/components/common/confirm-button/confirm-button.component.html create mode 100644 src-ui/src/app/components/common/confirm-button/confirm-button.component.scss create mode 100644 src-ui/src/app/components/common/confirm-button/confirm-button.component.spec.ts create mode 100644 src-ui/src/app/components/common/confirm-button/confirm-button.component.ts diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index a959d9fb2..3739605b3 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -458,7 +458,7 @@ src/app/components/admin/settings/settings.component.html - 346 + 354 src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.html @@ -498,11 +498,11 @@ src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 159 + 167 src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 92 + 99 src/app/components/document-detail/document-detail.component.html @@ -600,7 +600,7 @@ src/app/components/admin/settings/settings.component.html - 334 + 342 src/app/components/admin/tasks/tasks.component.html @@ -644,19 +644,19 @@ src/app/components/manage/management-list/management-list.component.html - 51 + 48 src/app/components/manage/management-list/management-list.component.html - 51 + 48 src/app/components/manage/management-list/management-list.component.html - 51 + 48 src/app/components/manage/management-list/management-list.component.html - 51 + 48 @@ -1048,11 +1048,11 @@ src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 104 + 112 src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 123 + 131 src/app/components/common/input/permissions/permissions-form/permissions-form.component.html @@ -1075,11 +1075,11 @@ src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 112 + 120 src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 131 + 139 src/app/components/common/input/permissions/permissions-form/permissions-form.component.html @@ -1105,7 +1105,7 @@ src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 137 + 145 src/app/components/common/input/permissions/permissions-form/permissions-form.component.html @@ -1262,35 +1262,35 @@ src/app/components/manage/management-list/management-list.component.html - 20 + 17 src/app/components/manage/management-list/management-list.component.html - 20 + 17 src/app/components/manage/management-list/management-list.component.html - 20 + 17 src/app/components/manage/management-list/management-list.component.html - 20 + 17 src/app/components/manage/management-list/management-list.component.html - 37 + 34 src/app/components/manage/management-list/management-list.component.html - 37 + 34 src/app/components/manage/management-list/management-list.component.html - 37 + 34 src/app/components/manage/management-list/management-list.component.html - 37 + 34 src/app/components/manage/workflows/workflows.component.html @@ -1346,7 +1346,7 @@ src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 60 + 64 src/app/components/document-detail/document-detail.component.html @@ -1370,19 +1370,19 @@ src/app/components/manage/management-list/management-list.component.html - 43 + 40 src/app/components/manage/management-list/management-list.component.html - 43 + 40 src/app/components/manage/management-list/management-list.component.html - 43 + 40 src/app/components/manage/management-list/management-list.component.html - 43 + 40 src/app/components/manage/workflows/workflows.component.html @@ -1393,7 +1393,7 @@ Delete src/app/components/admin/settings/settings.component.html - 322 + 324 src/app/components/admin/users-groups/users-groups.component.html @@ -1413,7 +1413,7 @@ src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 80 + 84 src/app/components/common/permissions-select/permissions-select.component.html @@ -1445,55 +1445,35 @@ src/app/components/manage/management-list/management-list.component.html - 9 + 81 src/app/components/manage/management-list/management-list.component.html - 9 + 81 src/app/components/manage/management-list/management-list.component.html - 9 + 81 src/app/components/manage/management-list/management-list.component.html - 9 + 81 src/app/components/manage/management-list/management-list.component.html - 84 + 93 src/app/components/manage/management-list/management-list.component.html - 84 + 93 src/app/components/manage/management-list/management-list.component.html - 84 + 93 src/app/components/manage/management-list/management-list.component.html - 84 - - - src/app/components/manage/management-list/management-list.component.html - 96 - - - src/app/components/manage/management-list/management-list.component.html - 96 - - - src/app/components/manage/management-list/management-list.component.html - 96 - - - src/app/components/manage/management-list/management-list.component.html - 96 - - - src/app/components/manage/management-list/management-list.component.ts - 208 + 93 src/app/components/manage/workflows/workflows.component.html @@ -1504,7 +1484,7 @@ No saved views defined. src/app/components/admin/settings/settings.component.html - 328 + 336 @@ -1897,35 +1877,35 @@ src/app/components/manage/management-list/management-list.component.html - 83 + 80 src/app/components/manage/management-list/management-list.component.html - 83 + 80 src/app/components/manage/management-list/management-list.component.html - 83 + 80 src/app/components/manage/management-list/management-list.component.html - 83 + 80 src/app/components/manage/management-list/management-list.component.html - 93 + 90 src/app/components/manage/management-list/management-list.component.html - 93 + 90 src/app/components/manage/management-list/management-list.component.html - 93 + 90 src/app/components/manage/management-list/management-list.component.html - 93 + 90 src/app/components/manage/workflows/workflows.component.html @@ -2019,10 +1999,6 @@ src/app/components/manage/mail/mail.component.ts 173 - - src/app/components/manage/management-list/management-list.component.ts - 320 - src/app/components/manage/workflows/workflows.component.ts 97 @@ -2058,10 +2034,6 @@ src/app/components/manage/mail/mail.component.ts 175 - - src/app/components/manage/management-list/management-list.component.ts - 322 - src/app/components/manage/workflows/workflows.component.ts 99 @@ -2204,19 +2176,19 @@ src/app/components/manage/management-list/management-list.component.html - 90 + 87 src/app/components/manage/management-list/management-list.component.html - 90 + 87 src/app/components/manage/management-list/management-list.component.html - 90 + 87 src/app/components/manage/management-list/management-list.component.html - 90 + 87 @@ -2461,6 +2433,13 @@ 55 + + Are you sure? + + src/app/components/common/confirm-button/confirm-button.component.ts + 20 + + Confirmation @@ -2539,7 +2518,7 @@ src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 158 + 166 src/app/components/common/permissions-dialog/permissions-dialog.component.html @@ -2547,7 +2526,7 @@ src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 91 + 98 src/app/components/common/select-dialog/select-dialog.component.html @@ -2723,7 +2702,7 @@ src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 182 + 190 @@ -3089,7 +3068,7 @@ src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 92 + 100 @@ -3107,7 +3086,7 @@ src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 93 + 101 @@ -3125,7 +3104,7 @@ src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 156 + 164 src/app/components/common/toasts/toasts.component.html @@ -3459,175 +3438,175 @@ Apply Actions: src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 66 + 70 Add Action src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 68 + 72 Action type src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 86 + 94 Assign title src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 90 + 98 Can include some placeholders, see <a target='_blank' href='https://docs.paperless-ngx.com/usage/#workflows'>documentation</a>. src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 90 + 98 Assign tags src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 91 + 99 Assign storage path src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 94 + 102 Assign custom fields src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 95 + 103 Assign owner src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 98 + 106 Assign view permissions src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 100 + 108 Assign edit permissions src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 119 + 127 Trigger type src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 166 + 174 Trigger for documents that match all filters specified below. src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 167 + 175 Filter filename src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 170 + 178 Apply to documents that match this filename. Wildcards such as *.pdf or *invoice* are allowed. Case insensitive. src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 170 + 178 Filter sources src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 172 + 180 Filter path src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 173 + 181 Apply to documents that match this path. Wildcards specified as * are allowed. Case-normalized.</a> src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 173 + 181 Filter mail rule src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 174 + 182 Apply to documents consumed via this mail rule. src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 174 + 182 Content matching algorithm src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 177 + 185 Content matching pattern src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 179 + 187 Has tags src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 188 + 196 Has correspondent src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 189 + 197 Has document type src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 190 + 198 @@ -3749,19 +3728,19 @@ src/app/components/manage/management-list/management-list.component.html - 12 + 9 src/app/components/manage/management-list/management-list.component.html - 12 + 9 src/app/components/manage/management-list/management-list.component.html - 12 + 9 src/app/components/manage/management-list/management-list.component.html - 12 + 9 @@ -4133,14 +4112,14 @@ Regenerate auth token src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 44 + 45 Copied! src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 48 + 53 src/app/components/common/share-links-dropdown/share-links-dropdown.component.html @@ -4151,28 +4130,21 @@ Warning: changing the token cannot be undone src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 50 + 55 Connected social accounts src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 54 + 59 Set a password before disconnecting social account. src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 58 - - - - Disconnect social account - - src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 68 + 63 @@ -4182,18 +4154,25 @@ 69 + + Disconnect social account + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 71 + + Warning: disconnecting social accounts cannot be undone src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 74 + 81 Connect new social account src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 79 + 86 @@ -5020,14 +4999,6 @@ src/app/components/document-detail/document-detail.component.ts 717 - - src/app/components/manage/management-list/management-list.component.ts - 204 - - - src/app/components/manage/management-list/management-list.component.ts - 318 - Do you really want to delete document ""? @@ -6220,109 +6191,109 @@ src/app/components/manage/management-list/management-list.component.ts - 305 + 285 Filter by: src/app/components/manage/management-list/management-list.component.html - 19 + 16 src/app/components/manage/management-list/management-list.component.html - 19 + 16 src/app/components/manage/management-list/management-list.component.html - 19 + 16 src/app/components/manage/management-list/management-list.component.html - 19 + 16 Matching src/app/components/manage/management-list/management-list.component.html - 38 + 35 src/app/components/manage/management-list/management-list.component.html - 38 + 35 src/app/components/manage/management-list/management-list.component.html - 38 + 35 src/app/components/manage/management-list/management-list.component.html - 38 + 35 Document count src/app/components/manage/management-list/management-list.component.html - 39 + 36 src/app/components/manage/management-list/management-list.component.html - 39 + 36 src/app/components/manage/management-list/management-list.component.html - 39 + 36 src/app/components/manage/management-list/management-list.component.html - 39 + 36 Filter Documents src/app/components/manage/management-list/management-list.component.html - 82 + 79 src/app/components/manage/management-list/management-list.component.html - 82 + 79 src/app/components/manage/management-list/management-list.component.html - 82 + 79 src/app/components/manage/management-list/management-list.component.html - 82 + 79 {VAR_PLURAL, plural, =1 {One } other { total }} src/app/components/manage/management-list/management-list.component.html - 110 + 113 src/app/components/manage/management-list/management-list.component.html - 110 + 113 src/app/components/manage/management-list/management-list.component.html - 110 + 113 src/app/components/manage/management-list/management-list.component.html - 110 + 113 Automatic src/app/components/manage/management-list/management-list.component.ts - 116 + 110 src/app/data/matching-model.ts @@ -6333,7 +6304,7 @@ None src/app/components/manage/management-list/management-list.component.ts - 118 + 112 src/app/data/matching-model.ts @@ -6344,70 +6315,42 @@ Successfully created . src/app/components/manage/management-list/management-list.component.ts - 161 + 155 Error occurred while creating . src/app/components/manage/management-list/management-list.component.ts - 166 + 160 Successfully updated . src/app/components/manage/management-list/management-list.component.ts - 181 + 175 Error occurred while saving . src/app/components/manage/management-list/management-list.component.ts - 186 - - - - Associated documents will not be deleted. - - src/app/components/manage/management-list/management-list.component.ts - 206 + 180 Error while deleting element src/app/components/manage/management-list/management-list.component.ts - 222 + 204 Permissions updated successfully src/app/components/manage/management-list/management-list.component.ts - 298 - - - - This operation will permanently delete all objects. - - src/app/components/manage/management-list/management-list.component.ts - 319 - - - - Objects deleted successfully - - src/app/components/manage/management-list/management-list.component.ts - 333 - - - - Error deleting objects - - src/app/components/manage/management-list/management-list.component.ts - 339 + 278 diff --git a/src-ui/src/app/app.module.ts b/src-ui/src/app/app.module.ts index a20a69eb8..395ddc04f 100644 --- a/src-ui/src/app/app.module.ts +++ b/src-ui/src/app/app.module.ts @@ -112,6 +112,7 @@ import { SwitchComponent } from './components/common/input/switch/switch.compone import { ConfigComponent } from './components/admin/config/config.component' import { FileComponent } from './components/common/input/file/file.component' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' +import { ConfirmButtonComponent } from './components/common/confirm-button/confirm-button.component' import { archive, arrowCounterclockwise, @@ -439,6 +440,7 @@ function initializeApp(settings: SettingsService) { SwitchComponent, ConfigComponent, FileComponent, + ConfirmButtonComponent, ], imports: [ BrowserModule, diff --git a/src-ui/src/app/components/admin/settings/settings.component.html b/src-ui/src/app/components/admin/settings/settings.component.html index 8b239e772..5b9ce7d9f 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.html +++ b/src-ui/src/app/components/admin/settings/settings.component.html @@ -319,7 +319,15 @@
    - + + +
    } diff --git a/src-ui/src/app/components/admin/settings/settings.component.spec.ts b/src-ui/src/app/components/admin/settings/settings.component.spec.ts index 6e105ed11..6a9ca36da 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.spec.ts +++ b/src-ui/src/app/components/admin/settings/settings.component.spec.ts @@ -38,6 +38,7 @@ import { PageHeaderComponent } from '../../common/page-header/page-header.compon import { SettingsComponent } from './settings.component' import { IfOwnerDirective } from 'src/app/directives/if-owner.directive' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' +import { ConfirmButtonComponent } from '../../common/confirm-button/confirm-button.component' const savedViews = [ { id: 1, name: 'view1', show_in_sidebar: true, show_on_dashboard: true }, @@ -83,6 +84,7 @@ describe('SettingsComponent', () => { PermissionsUserComponent, PermissionsGroupComponent, IfOwnerDirective, + ConfirmButtonComponent, ], providers: [CustomDatePipe, DatePipe, PermissionsGuard], imports: [ diff --git a/src-ui/src/app/components/common/confirm-button/confirm-button.component.html b/src-ui/src/app/components/common/confirm-button/confirm-button.component.html new file mode 100644 index 000000000..a82a06c4b --- /dev/null +++ b/src-ui/src/app/components/common/confirm-button/confirm-button.component.html @@ -0,0 +1,22 @@ + + + +
    + {{confirmMessage}}  +
    +
    diff --git a/src-ui/src/app/components/common/confirm-button/confirm-button.component.scss b/src-ui/src/app/components/common/confirm-button/confirm-button.component.scss new file mode 100644 index 000000000..14d19be51 --- /dev/null +++ b/src-ui/src/app/components/common/confirm-button/confirm-button.component.scss @@ -0,0 +1,12 @@ +// Taken from bootstrap rules, obv +::ng-deep .input-group > pngx-confirm-button:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) > button, +::ng-deep .btn-group > pngx-confirm-button:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) > button { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + + ::ng-deep .input-group:not(.has-validation) > pngx-confirm-button:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating) > button, + ::ng-deep .btn-group:not(.has-validation) > pngx-confirm-button:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating) > button { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } diff --git a/src-ui/src/app/components/common/confirm-button/confirm-button.component.spec.ts b/src-ui/src/app/components/common/confirm-button/confirm-button.component.spec.ts new file mode 100644 index 000000000..d67777d45 --- /dev/null +++ b/src-ui/src/app/components/common/confirm-button/confirm-button.component.spec.ts @@ -0,0 +1,37 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing' + +import { ConfirmButtonComponent } from './confirm-button.component' +import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap' +import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' + +describe('ConfirmButtonComponent', () => { + let component: ConfirmButtonComponent + let fixture: ComponentFixture + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ConfirmButtonComponent], + imports: [NgbPopoverModule, NgxBootstrapIconsModule.pick(allIcons)], + }).compileComponents() + + fixture = TestBed.createComponent(ConfirmButtonComponent) + component = fixture.componentInstance + fixture.detectChanges() + }) + + it('should show confirm on click', () => { + expect(component.popover.isOpen()).toBeFalsy() + expect(component.confirming).toBeFalsy() + component.onClick(new MouseEvent('click')) + expect(component.popover.isOpen()).toBeTruthy() + expect(component.confirming).toBeTruthy() + }) + + it('should emit confirm on confirm', () => { + const confirmSpy = jest.spyOn(component.confirm, 'emit') + component.onConfirm(new MouseEvent('click')) + expect(confirmSpy).toHaveBeenCalled() + expect(component.popover.isOpen()).toBeFalsy() + expect(component.confirming).toBeFalsy() + }) +}) diff --git a/src-ui/src/app/components/common/confirm-button/confirm-button.component.ts b/src-ui/src/app/components/common/confirm-button/confirm-button.component.ts new file mode 100644 index 000000000..f6746f99d --- /dev/null +++ b/src-ui/src/app/components/common/confirm-button/confirm-button.component.ts @@ -0,0 +1,55 @@ +import { + Component, + EventEmitter, + Input, + Output, + ViewChild, +} from '@angular/core' +import { NgbPopover } from '@ng-bootstrap/ng-bootstrap' + +@Component({ + selector: 'pngx-confirm-button', + templateUrl: './confirm-button.component.html', + styleUrl: './confirm-button.component.scss', +}) +export class ConfirmButtonComponent { + @Input() + label: string + + @Input() + confirmMessage: string = $localize`Are you sure?` + + @Input() + buttonClasses: string = 'btn-primary' + + @Input() + iconName: string + + @Input() + disabled: boolean = false + + @Output() + confirm: EventEmitter = new EventEmitter() + + @ViewChild('popover') popover: NgbPopover + + public confirming: boolean = false + + public onClick(event: MouseEvent) { + if (!this.confirming) { + this.confirming = true + this.popover.open() + } + + event.preventDefault() + event.stopImmediatePropagation() + } + + public onConfirm(event: MouseEvent) { + this.confirm.emit() + this.confirming = false + + event.preventDefault() + event.stopImmediatePropagation() + } +} diff --git a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html index c393df944..623119605 100644 --- a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html +++ b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html @@ -38,9 +38,13 @@ @if(trigger.id > -1) { ID: {{trigger.id}} } - + +
    @@ -76,9 +80,13 @@ @if(action.id > -1) { ID: {{action.id}} } - + +
    diff --git a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts index a901e20ac..f7eeb1bf0 100644 --- a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts @@ -38,6 +38,7 @@ import { WorkflowActionType, } from 'src/app/data/workflow-action' import { MATCHING_ALGORITHMS, MATCH_AUTO } from 'src/app/data/matching-model' +import { ConfirmButtonComponent } from '../../confirm-button/confirm-button.component' const workflow: Workflow = { name: 'Workflow 1', @@ -85,6 +86,7 @@ describe('WorkflowEditDialogComponent', () => { PermissionsUserComponent, PermissionsGroupComponent, SafeHtmlPipe, + ConfirmButtonComponent, ], providers: [ NgbActiveModal, diff --git a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html index 6b06dfa8e..713d68864 100644 --- a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html +++ b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html @@ -41,9 +41,14 @@ } Copy - + +
    Copied! @@ -60,14 +65,16 @@ [disablePopover]="hasUsablePassword" triggers="mouseenter:mouseleave"> {{account.name}} ({{account.provider}}) - + + } diff --git a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.spec.ts index 36888d4bd..b57cf07f2 100644 --- a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.spec.ts @@ -21,6 +21,7 @@ import { of, throwError } from 'rxjs' import { ToastService } from 'src/app/services/toast.service' import { Clipboard } from '@angular/cdk/clipboard' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' +import { ConfirmButtonComponent } from '../confirm-button/confirm-button.component' const socialAccount = { id: 1, @@ -52,6 +53,7 @@ describe('ProfileEditDialogComponent', () => { ProfileEditDialogComponent, TextComponent, PasswordComponent, + ConfirmButtonComponent, ], providers: [NgbActiveModal], imports: [ diff --git a/src-ui/src/app/components/manage/management-list/management-list.component.html b/src-ui/src/app/components/manage/management-list/management-list.component.html index 58101c388..d627a1540 100644 --- a/src-ui/src/app/components/manage/management-list/management-list.component.html +++ b/src-ui/src/app/components/manage/management-list/management-list.component.html @@ -88,33 +88,39 @@
    - - -
    - - - } - - - - - @if (!isLoading) { -
    - @if (collectionSize > 0) { -
    - {collectionSize, plural, =1 {One {{typeName}}} other {{{collectionSize || 0}} total {{typeNamePlural}}}} - @if (selectedObjects.size > 0) { -  ({{selectedObjects.size}} selected) - } + + + +
    - } - @if (collectionSize > 20) { - - } -
    + + } + + + + + @if (!isLoading) { +
    + @if (collectionSize > 0) { +
    + {collectionSize, plural, =1 {One {{typeName}}} other {{{collectionSize || 0}} total {{typeNamePlural}}}} + @if (selectedObjects.size > 0) { +  ({{selectedObjects.size}} selected) + } +
    + } + @if (collectionSize > 20) { + + } +
    + } diff --git a/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts b/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts index 710d3018a..280c40ca8 100644 --- a/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts +++ b/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts @@ -13,6 +13,7 @@ import { NgbModalModule, NgbModalRef, NgbPaginationModule, + NgbPopoverModule, } from '@ng-bootstrap/ng-bootstrap' import { of, throwError } from 'rxjs' import { Tag } from 'src/app/data/tag' @@ -37,6 +38,7 @@ import { MATCH_NONE } from 'src/app/data/matching-model' import { MATCH_LITERAL } from 'src/app/data/matching-model' import { PermissionsDialogComponent } from '../../common/permissions-dialog/permissions-dialog.component' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' +import { ConfirmButtonComponent } from '../../common/confirm-button/confirm-button.component' import { BulkEditObjectOperation } from 'src/app/services/rest/abstract-name-filter-service' const tags: Tag[] = [ @@ -76,6 +78,7 @@ describe('ManagementListComponent', () => { SafeHtmlPipe, ConfirmDialogComponent, PermissionsDialogComponent, + ConfirmButtonComponent, ], providers: [ { @@ -97,6 +100,7 @@ describe('ManagementListComponent', () => { NgbModalModule, RouterTestingModule.withRoutes(routes), NgxBootstrapIconsModule.pick(allIcons), + NgbPopoverModule, ], }).compileComponents() @@ -193,27 +197,23 @@ describe('ManagementListComponent', () => { }) it('should support delete, show notification on error / success', () => { - let modal: NgbModalRef - modalService.activeInstances.subscribe((m) => (modal = m[m.length - 1])) const toastErrorSpy = jest.spyOn(toastService, 'showError') const deleteSpy = jest.spyOn(tagService, 'delete') const reloadSpy = jest.spyOn(component, 'reloadData') - const deleteButton = fixture.debugElement.queryAll(By.css('button'))[8] - deleteButton.triggerEventHandler('click') - - expect(modal).not.toBeUndefined() - const editDialog = modal.componentInstance as ConfirmDialogComponent + const deleteButton = fixture.debugElement.query( + By.directive(ConfirmButtonComponent) + ) // fail first deleteSpy.mockReturnValueOnce(throwError(() => new Error('error deleting'))) - editDialog.confirmClicked.emit() + deleteButton.nativeElement.dispatchEvent(new Event('confirm')) expect(toastErrorSpy).toHaveBeenCalled() expect(reloadSpy).not.toHaveBeenCalled() // succeed deleteSpy.mockReturnValueOnce(of(true)) - editDialog.confirmClicked.emit() + deleteButton.nativeElement.dispatchEvent(new Event('confirm')) expect(reloadSpy).toHaveBeenCalled() }) diff --git a/src-ui/src/app/components/manage/management-list/management-list.component.ts b/src-ui/src/app/components/manage/management-list/management-list.component.ts index 0b0365f06..8f0947f1c 100644 --- a/src-ui/src/app/components/manage/management-list/management-list.component.ts +++ b/src-ui/src/app/components/manage/management-list/management-list.component.ts @@ -15,10 +15,7 @@ import { MATCH_NONE, } from 'src/app/data/matching-model' import { ObjectWithId } from 'src/app/data/object-with-id' -import { - ObjectWithPermissions, - PermissionsObject, -} from 'src/app/data/object-with-permissions' +import { ObjectWithPermissions } from 'src/app/data/object-with-permissions' import { SortableDirective, SortEvent, @@ -197,34 +194,21 @@ export abstract class ManagementListComponent ]) } - openDeleteDialog(object: T) { - var activeModal = this.modalService.open(ConfirmDialogComponent, { - backdrop: 'static', - }) - activeModal.componentInstance.title = $localize`Confirm delete` - activeModal.componentInstance.messageBold = this.getDeleteMessage(object) - activeModal.componentInstance.message = $localize`Associated documents will not be deleted.` - activeModal.componentInstance.btnClass = 'btn-danger' - activeModal.componentInstance.btnCaption = $localize`Delete` - activeModal.componentInstance.confirmClicked.subscribe(() => { - activeModal.componentInstance.buttonsEnabled = false - this.service - .delete(object) - .pipe(takeUntil(this.unsubscribeNotifier)) - .subscribe({ - next: () => { - activeModal.close() - this.reloadData() - }, - error: (error) => { - activeModal.componentInstance.buttonsEnabled = true - this.toastService.showError( - $localize`Error while deleting element`, - error - ) - }, - }) - }) + deleteObject(object: T) { + this.service + .delete(object) + .pipe(takeUntil(this.unsubscribeNotifier)) + .subscribe({ + next: () => { + this.reloadData() + }, + error: (error) => { + this.toastService.showError( + $localize`Error while deleting element`, + error + ) + }, + }) } get nameFilter() { From 6587470033f57af3987be46f6ec8fdf996247d96 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 8 Feb 2024 11:56:55 -0800 Subject: [PATCH 37/41] Update translation strings --- src-ui/messages.xlf | 187 ++++++++++++++++--------- src/locale/en_US/LC_MESSAGES/django.po | 90 ++++++------ 2 files changed, 163 insertions(+), 114 deletions(-) diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 3739605b3..3a6d6f8df 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -644,19 +644,19 @@ src/app/components/manage/management-list/management-list.component.html - 48 + 51 src/app/components/manage/management-list/management-list.component.html - 48 + 51 src/app/components/manage/management-list/management-list.component.html - 48 + 51 src/app/components/manage/management-list/management-list.component.html - 48 + 51
    @@ -1262,35 +1262,35 @@ src/app/components/manage/management-list/management-list.component.html - 17 + 20 src/app/components/manage/management-list/management-list.component.html - 17 + 20 src/app/components/manage/management-list/management-list.component.html - 17 + 20 src/app/components/manage/management-list/management-list.component.html - 17 + 20 src/app/components/manage/management-list/management-list.component.html - 34 + 37 src/app/components/manage/management-list/management-list.component.html - 34 + 37 src/app/components/manage/management-list/management-list.component.html - 34 + 37 src/app/components/manage/management-list/management-list.component.html - 34 + 37 src/app/components/manage/workflows/workflows.component.html @@ -1370,19 +1370,19 @@ src/app/components/manage/management-list/management-list.component.html - 40 + 43 src/app/components/manage/management-list/management-list.component.html - 40 + 43 src/app/components/manage/management-list/management-list.component.html - 40 + 43 src/app/components/manage/management-list/management-list.component.html - 40 + 43 src/app/components/manage/workflows/workflows.component.html @@ -1445,35 +1445,51 @@ src/app/components/manage/management-list/management-list.component.html - 81 + 9 src/app/components/manage/management-list/management-list.component.html - 81 + 9 src/app/components/manage/management-list/management-list.component.html - 81 + 9 src/app/components/manage/management-list/management-list.component.html - 81 + 9 src/app/components/manage/management-list/management-list.component.html - 93 + 84 src/app/components/manage/management-list/management-list.component.html - 93 + 84 src/app/components/manage/management-list/management-list.component.html - 93 + 84 src/app/components/manage/management-list/management-list.component.html - 93 + 84 + + + src/app/components/manage/management-list/management-list.component.html + 96 + + + src/app/components/manage/management-list/management-list.component.html + 96 + + + src/app/components/manage/management-list/management-list.component.html + 96 + + + src/app/components/manage/management-list/management-list.component.html + 96 src/app/components/manage/workflows/workflows.component.html @@ -1877,35 +1893,35 @@ src/app/components/manage/management-list/management-list.component.html - 80 + 83 src/app/components/manage/management-list/management-list.component.html - 80 + 83 src/app/components/manage/management-list/management-list.component.html - 80 + 83 src/app/components/manage/management-list/management-list.component.html - 80 + 83 src/app/components/manage/management-list/management-list.component.html - 90 + 93 src/app/components/manage/management-list/management-list.component.html - 90 + 93 src/app/components/manage/management-list/management-list.component.html - 90 + 93 src/app/components/manage/management-list/management-list.component.html - 90 + 93 src/app/components/manage/workflows/workflows.component.html @@ -1999,6 +2015,10 @@ src/app/components/manage/mail/mail.component.ts 173 + + src/app/components/manage/management-list/management-list.component.ts + 304 + src/app/components/manage/workflows/workflows.component.ts 97 @@ -2034,6 +2054,10 @@ src/app/components/manage/mail/mail.component.ts 175 + + src/app/components/manage/management-list/management-list.component.ts + 306 + src/app/components/manage/workflows/workflows.component.ts 99 @@ -2176,19 +2200,19 @@ src/app/components/manage/management-list/management-list.component.html - 87 + 90 src/app/components/manage/management-list/management-list.component.html - 87 + 90 src/app/components/manage/management-list/management-list.component.html - 87 + 90 src/app/components/manage/management-list/management-list.component.html - 87 + 90 @@ -3728,19 +3752,19 @@ src/app/components/manage/management-list/management-list.component.html - 9 + 12 src/app/components/manage/management-list/management-list.component.html - 9 + 12 src/app/components/manage/management-list/management-list.component.html - 9 + 12 src/app/components/manage/management-list/management-list.component.html - 9 + 12 @@ -4999,6 +5023,10 @@ src/app/components/document-detail/document-detail.component.ts 717 + + src/app/components/manage/management-list/management-list.component.ts + 302 + Do you really want to delete document ""? @@ -6191,109 +6219,109 @@ src/app/components/manage/management-list/management-list.component.ts - 285 + 289 Filter by: src/app/components/manage/management-list/management-list.component.html - 16 + 19 src/app/components/manage/management-list/management-list.component.html - 16 + 19 src/app/components/manage/management-list/management-list.component.html - 16 + 19 src/app/components/manage/management-list/management-list.component.html - 16 + 19 Matching src/app/components/manage/management-list/management-list.component.html - 35 + 38 src/app/components/manage/management-list/management-list.component.html - 35 + 38 src/app/components/manage/management-list/management-list.component.html - 35 + 38 src/app/components/manage/management-list/management-list.component.html - 35 + 38 Document count src/app/components/manage/management-list/management-list.component.html - 36 + 39 src/app/components/manage/management-list/management-list.component.html - 36 + 39 src/app/components/manage/management-list/management-list.component.html - 36 + 39 src/app/components/manage/management-list/management-list.component.html - 36 + 39 Filter Documents src/app/components/manage/management-list/management-list.component.html - 79 + 82 src/app/components/manage/management-list/management-list.component.html - 79 + 82 src/app/components/manage/management-list/management-list.component.html - 79 + 82 src/app/components/manage/management-list/management-list.component.html - 79 + 82 {VAR_PLURAL, plural, =1 {One } other { total }} src/app/components/manage/management-list/management-list.component.html - 113 + 116 src/app/components/manage/management-list/management-list.component.html - 113 + 116 src/app/components/manage/management-list/management-list.component.html - 113 + 116 src/app/components/manage/management-list/management-list.component.html - 113 + 116 Automatic src/app/components/manage/management-list/management-list.component.ts - 110 + 113 src/app/data/matching-model.ts @@ -6304,7 +6332,7 @@ None src/app/components/manage/management-list/management-list.component.ts - 112 + 115 src/app/data/matching-model.ts @@ -6315,42 +6343,63 @@ Successfully created . src/app/components/manage/management-list/management-list.component.ts - 155 + 158 Error occurred while creating . src/app/components/manage/management-list/management-list.component.ts - 160 + 163 Successfully updated . src/app/components/manage/management-list/management-list.component.ts - 175 + 178 Error occurred while saving . src/app/components/manage/management-list/management-list.component.ts - 180 + 183 Error while deleting element src/app/components/manage/management-list/management-list.component.ts - 204 + 207 Permissions updated successfully src/app/components/manage/management-list/management-list.component.ts - 278 + 282 + + + + This operation will permanently delete all objects. + + src/app/components/manage/management-list/management-list.component.ts + 303 + + + + Objects deleted successfully + + src/app/components/manage/management-list/management-list.component.ts + 317 + + + + Error deleting objects + + src/app/components/manage/management-list/management-list.component.ts + 323 diff --git a/src/locale/en_US/LC_MESSAGES/django.po b/src/locale/en_US/LC_MESSAGES/django.po index 7ee0c0d8b..403ba262e 100644 --- a/src/locale/en_US/LC_MESSAGES/django.po +++ b/src/locale/en_US/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: paperless-ngx\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-02-07 06:20+0000\n" +"POT-Creation-Date: 2024-02-08 11:56-0800\n" "PO-Revision-Date: 2022-02-17 04:17\n" "Last-Translator: \n" "Language-Team: English\n" @@ -768,21 +768,21 @@ msgstr "" msgid "enabled" msgstr "" -#: documents/serialisers.py:112 +#: documents/serialisers.py:113 #, python-format msgid "Invalid regular expression: %(error)s" msgstr "" -#: documents/serialisers.py:406 +#: documents/serialisers.py:407 msgid "Invalid color." msgstr "" -#: documents/serialisers.py:1060 +#: documents/serialisers.py:1061 #, python-format msgid "File type %(type)s not supported" msgstr "" -#: documents/serialisers.py:1163 +#: documents/serialisers.py:1164 msgid "Invalid variable detected." msgstr "" @@ -1118,131 +1118,131 @@ msgstr "" msgid "paperless application settings" msgstr "" -#: paperless/settings.py:658 +#: paperless/settings.py:642 msgid "English (US)" msgstr "" -#: paperless/settings.py:659 +#: paperless/settings.py:643 msgid "Arabic" msgstr "" -#: paperless/settings.py:660 +#: paperless/settings.py:644 msgid "Afrikaans" msgstr "" -#: paperless/settings.py:661 +#: paperless/settings.py:645 msgid "Belarusian" msgstr "" -#: paperless/settings.py:662 +#: paperless/settings.py:646 msgid "Bulgarian" msgstr "" -#: paperless/settings.py:663 +#: paperless/settings.py:647 msgid "Catalan" msgstr "" -#: paperless/settings.py:664 +#: paperless/settings.py:648 msgid "Czech" msgstr "" -#: paperless/settings.py:665 +#: paperless/settings.py:649 msgid "Danish" msgstr "" -#: paperless/settings.py:666 +#: paperless/settings.py:650 msgid "German" msgstr "" -#: paperless/settings.py:667 +#: paperless/settings.py:651 msgid "Greek" msgstr "" -#: paperless/settings.py:668 +#: paperless/settings.py:652 msgid "English (GB)" msgstr "" -#: paperless/settings.py:669 +#: paperless/settings.py:653 msgid "Spanish" msgstr "" -#: paperless/settings.py:670 +#: paperless/settings.py:654 msgid "Finnish" msgstr "" -#: paperless/settings.py:671 +#: paperless/settings.py:655 msgid "French" msgstr "" -#: paperless/settings.py:672 +#: paperless/settings.py:656 msgid "Hungarian" msgstr "" -#: paperless/settings.py:673 +#: paperless/settings.py:657 msgid "Italian" msgstr "" -#: paperless/settings.py:674 +#: paperless/settings.py:658 msgid "Japanese" msgstr "" -#: paperless/settings.py:675 +#: paperless/settings.py:659 msgid "Luxembourgish" msgstr "" -#: paperless/settings.py:676 +#: paperless/settings.py:660 msgid "Norwegian" msgstr "" -#: paperless/settings.py:677 +#: paperless/settings.py:661 msgid "Dutch" msgstr "" -#: paperless/settings.py:678 +#: paperless/settings.py:662 msgid "Polish" msgstr "" -#: paperless/settings.py:679 +#: paperless/settings.py:663 msgid "Portuguese (Brazil)" msgstr "" -#: paperless/settings.py:680 +#: paperless/settings.py:664 msgid "Portuguese" msgstr "" -#: paperless/settings.py:681 +#: paperless/settings.py:665 msgid "Romanian" msgstr "" -#: paperless/settings.py:682 +#: paperless/settings.py:666 msgid "Russian" msgstr "" -#: paperless/settings.py:683 +#: paperless/settings.py:667 msgid "Slovak" msgstr "" -#: paperless/settings.py:684 +#: paperless/settings.py:668 msgid "Slovenian" msgstr "" -#: paperless/settings.py:685 +#: paperless/settings.py:669 msgid "Serbian" msgstr "" -#: paperless/settings.py:686 +#: paperless/settings.py:670 msgid "Swedish" msgstr "" -#: paperless/settings.py:687 +#: paperless/settings.py:671 msgid "Turkish" msgstr "" -#: paperless/settings.py:688 +#: paperless/settings.py:672 msgid "Ukrainian" msgstr "" -#: paperless/settings.py:689 +#: paperless/settings.py:673 msgid "Chinese Simplified" msgstr "" @@ -1250,38 +1250,38 @@ msgstr "" msgid "Paperless-ngx administration" msgstr "" -#: paperless_mail/admin.py:41 +#: paperless_mail/admin.py:39 msgid "Authentication" msgstr "" -#: paperless_mail/admin.py:44 +#: paperless_mail/admin.py:42 msgid "Advanced settings" msgstr "" -#: paperless_mail/admin.py:60 +#: paperless_mail/admin.py:58 msgid "Filter" msgstr "" -#: paperless_mail/admin.py:63 +#: paperless_mail/admin.py:61 msgid "" "Paperless will only process mails that match ALL of the filters given below." msgstr "" -#: paperless_mail/admin.py:80 +#: paperless_mail/admin.py:78 msgid "Actions" msgstr "" -#: paperless_mail/admin.py:83 +#: paperless_mail/admin.py:81 msgid "" "The action applied to the mail. This action is only performed when the mail " "body or attachments were consumed from the mail." msgstr "" -#: paperless_mail/admin.py:91 +#: paperless_mail/admin.py:89 msgid "Metadata" msgstr "" -#: paperless_mail/admin.py:94 +#: paperless_mail/admin.py:92 msgid "" "Assign metadata to documents consumed from this rule automatically. If you " "do not assign tags, types or correspondents here, paperless will still " From 4855f4b8b17d47fb98d1f4e5a02cc220c2c45d27 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Fri, 9 Feb 2024 08:57:09 -0800 Subject: [PATCH 38/41] Fix: only check workflow trigger source if not empty (#5701) --- src/documents/matching.py | 4 +- src/documents/serialisers.py | 3 -- src/documents/tests/test_api_workflows.py | 52 ----------------------- 3 files changed, 3 insertions(+), 56 deletions(-) diff --git a/src/documents/matching.py b/src/documents/matching.py index 81154b8f4..6ffa1b3aa 100644 --- a/src/documents/matching.py +++ b/src/documents/matching.py @@ -258,7 +258,9 @@ def consumable_document_matches_workflow( reason = "" # Document source vs trigger source - if document.source not in [int(x) for x in list(trigger.sources)]: + if len(trigger.sources) > 0 and document.source not in [ + int(x) for x in list(trigger.sources) + ]: reason = ( f"Document source {document.source.name} not in" f" {[DocumentSource(int(x)).name for x in trigger.sources]}", diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 5fa104640..891891846 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -1410,9 +1410,6 @@ class WorkflowTriggerSerializer(serializers.ModelSerializer): ] def validate(self, attrs): - if ("filter_mailrule") in attrs and attrs["filter_mailrule"] is not None: - attrs["sources"] = {DocumentSource.MailFetch.value} - # Empty strings treated as None to avoid unexpected behavior if ( "filter_filename" in attrs diff --git a/src/documents/tests/test_api_workflows.py b/src/documents/tests/test_api_workflows.py index 21e887c24..0751d0df5 100644 --- a/src/documents/tests/test_api_workflows.py +++ b/src/documents/tests/test_api_workflows.py @@ -15,8 +15,6 @@ from documents.models import Workflow from documents.models import WorkflowAction from documents.models import WorkflowTrigger from documents.tests.utils import DirectoriesMixin -from paperless_mail.models import MailAccount -from paperless_mail.models import MailRule class TestApiWorkflows(DirectoriesMixin, APITestCase): @@ -344,56 +342,6 @@ class TestApiWorkflows(DirectoriesMixin, APITestCase): self.assertEqual(trigger2.filter_path, "*/test/*") self.assertIsNone(trigger2.filter_filename) - def test_api_create_workflow_trigger_with_mailrule(self): - """ - GIVEN: - - API request to create a workflow trigger with a mail rule but no MailFetch source - WHEN: - - API is called - THEN: - - New trigger is created with MailFetch as source - """ - account1 = MailAccount.objects.create( - name="Email1", - username="username1", - password="password1", - imap_server="server.example.com", - imap_port=443, - imap_security=MailAccount.ImapSecurity.SSL, - character_set="UTF-8", - ) - rule1 = MailRule.objects.create( - name="Rule1", - account=account1, - folder="INBOX", - filter_from="from@example.com", - filter_to="someone@somewhere.com", - filter_subject="subject", - filter_body="body", - filter_attachment_filename_include="file.pdf", - maximum_age=30, - action=MailRule.MailAction.MARK_READ, - assign_title_from=MailRule.TitleSource.FROM_SUBJECT, - assign_correspondent_from=MailRule.CorrespondentSource.FROM_NOTHING, - order=0, - attachment_type=MailRule.AttachmentProcessing.ATTACHMENTS_ONLY, - ) - response = self.client.post( - self.ENDPOINT_TRIGGERS, - json.dumps( - { - "type": WorkflowTrigger.WorkflowTriggerType.CONSUMPTION, - "sources": [DocumentSource.ApiUpload], - "filter_mailrule": rule1.pk, - }, - ), - content_type="application/json", - ) - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(WorkflowTrigger.objects.count(), 2) - trigger = WorkflowTrigger.objects.get(id=response.data["id"]) - self.assertEqual(trigger.sources, [int(DocumentSource.MailFetch).__str__()]) - def test_api_update_workflow_nested_triggers_actions(self): """ GIVEN: From cd3b1a221ea805beb6433c90618a47116a4d6575 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Fri, 9 Feb 2024 11:35:32 -0800 Subject: [PATCH 39/41] Change: show preview even if metadata fails (#5706) --- .../app/components/document-detail/document-detail.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts index a1162ab7f..6630f0ad6 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.ts @@ -487,7 +487,7 @@ export class DocumentDetailComponent this.metadata = result }, error: (error) => { - this.metadata = null + this.metadata = {} // allow display to fallback to tag this.toastService.showError( $localize`Error retrieving metadata`, error From 0b1523f4e5dc37ab936c1f6ec8fe49145a5ca057 Mon Sep 17 00:00:00 2001 From: Trenton H <797416+stumpylog@users.noreply.github.com> Date: Fri, 9 Feb 2024 12:08:23 -0800 Subject: [PATCH 40/41] Fix: Test metadata items for Unicode issues (#5707) Test each key for unicode issues and reject ones which will fail inside DRF --- src/paperless_mail/parsers.py | 5 +++++ src/paperless_tesseract/parsers.py | 16 +++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/paperless_mail/parsers.py b/src/paperless_mail/parsers.py index 92fb90bb1..b8cf12980 100644 --- a/src/paperless_mail/parsers.py +++ b/src/paperless_mail/parsers.py @@ -69,6 +69,11 @@ class MailDocumentParser(DocumentParser): for key, value in mail.headers.items(): value = ", ".join(i for i in value) + try: + value.encode("utf-8") + except UnicodeEncodeError as e: # pragma: no cover + self.log.debug(f"Skipping header {key}: {e}") + continue result.append( { diff --git a/src/paperless_tesseract/parsers.py b/src/paperless_tesseract/parsers.py index b6baa3289..09086585e 100644 --- a/src/paperless_tesseract/parsers.py +++ b/src/paperless_tesseract/parsers.py @@ -55,11 +55,21 @@ class RasterisedDocumentParser(DocumentParser): value = str(value) try: m = namespace_pattern.match(key) + if m is None: # pragma: no cover + continue + namespace = m.group(1) + key_value = m.group(2) + try: + namespace.encode("utf-8") + key_value.encode("utf-8") + except UnicodeEncodeError as e: # pragma: no cover + self.log.debug(f"Skipping metadata key {key}: {e}") + continue result.append( { - "namespace": m.group(1), - "prefix": meta.REVERSE_NS[m.group(1)], - "key": m.group(2), + "namespace": namespace, + "prefix": meta.REVERSE_NS[namespace], + "key": key_value, "value": value, }, ) From 13201dbfffc939a8a415a929325727b86ae842aa Mon Sep 17 00:00:00 2001 From: Trenton H <797416+stumpylog@users.noreply.github.com> Date: Sat, 10 Feb 2024 11:02:40 -0800 Subject: [PATCH 41/41] Ensure all creations of directories create the parents too (#5711) --- src/documents/consumer.py | 8 ++++---- src/documents/index.py | 2 +- .../management/commands/document_archiver.py | 3 +-- .../management/commands/document_exporter.py | 2 +- .../management/commands/document_importer.py | 6 +++--- src/documents/parsers.py | 2 +- src/documents/tests/test_date_parsing.py | 15 --------------- src/documents/tests/test_file_handling.py | 12 ++++++------ src/documents/views.py | 4 ++-- src/paperless/settings.py | 2 +- src/paperless_mail/mail.py | 4 ++-- 11 files changed, 22 insertions(+), 38 deletions(-) diff --git a/src/documents/consumer.py b/src/documents/consumer.py index 01b25edea..92461484c 100644 --- a/src/documents/consumer.py +++ b/src/documents/consumer.py @@ -233,10 +233,10 @@ class Consumer(LoggingMixin): """ Ensure all required directories exist before attempting to use them """ - os.makedirs(settings.SCRATCH_DIR, exist_ok=True) - os.makedirs(settings.THUMBNAIL_DIR, exist_ok=True) - os.makedirs(settings.ORIGINALS_DIR, exist_ok=True) - os.makedirs(settings.ARCHIVE_DIR, exist_ok=True) + settings.SCRATCH_DIR.mkdir(parents=True, exist_ok=True) + settings.THUMBNAIL_DIR.mkdir(parents=True, exist_ok=True) + settings.ORIGINALS_DIR.mkdir(parents=True, exist_ok=True) + settings.ARCHIVE_DIR.mkdir(parents=True, exist_ok=True) def pre_check_asn_value(self): """ diff --git a/src/documents/index.py b/src/documents/index.py index 90bc15e7b..de651c13d 100644 --- a/src/documents/index.py +++ b/src/documents/index.py @@ -88,7 +88,7 @@ def open_index(recreate=False) -> FileIndex: logger.exception("Error while opening the index, recreating.") if not os.path.isdir(settings.INDEX_DIR): - os.makedirs(settings.INDEX_DIR, exist_ok=True) + settings.INDEX_DIR.mkdir(parents=True, exist_ok=True) return create_in(settings.INDEX_DIR, get_schema()) diff --git a/src/documents/management/commands/document_archiver.py b/src/documents/management/commands/document_archiver.py index 96b3b50ab..12677dd79 100644 --- a/src/documents/management/commands/document_archiver.py +++ b/src/documents/management/commands/document_archiver.py @@ -1,6 +1,5 @@ import logging import multiprocessing -import os import tqdm from django import db @@ -52,7 +51,7 @@ class Command(MultiProcessMixin, ProgressBarMixin, BaseCommand): self.handle_processes_mixin(**options) self.handle_progress_bar_mixin(**options) - os.makedirs(settings.SCRATCH_DIR, exist_ok=True) + settings.SCRATCH_DIR.mkdir(parents=True, exist_ok=True) overwrite = options["overwrite"] diff --git a/src/documents/management/commands/document_exporter.py b/src/documents/management/commands/document_exporter.py index 9bdbd1c51..faa42ba31 100644 --- a/src/documents/management/commands/document_exporter.py +++ b/src/documents/management/commands/document_exporter.py @@ -182,7 +182,7 @@ class Command(BaseCommand): if self.zip_export: self.original_target = self.target - os.makedirs(settings.SCRATCH_DIR, exist_ok=True) + settings.SCRATCH_DIR.mkdir(parents=True, exist_ok=True) temp_dir = tempfile.TemporaryDirectory( dir=settings.SCRATCH_DIR, prefix="paperless-export", diff --git a/src/documents/management/commands/document_importer.py b/src/documents/management/commands/document_importer.py index c166ec0cb..88ac2b91a 100644 --- a/src/documents/management/commands/document_importer.py +++ b/src/documents/management/commands/document_importer.py @@ -243,9 +243,9 @@ class Command(BaseCommand): ) from e def _import_files_from_manifest(self, progress_bar_disable): - os.makedirs(settings.ORIGINALS_DIR, exist_ok=True) - os.makedirs(settings.THUMBNAIL_DIR, exist_ok=True) - os.makedirs(settings.ARCHIVE_DIR, exist_ok=True) + settings.ORIGINALS_DIR.mkdir(parents=True, exist_ok=True) + settings.THUMBNAIL_DIR.mkdir(parents=True, exist_ok=True) + settings.ARCHIVE_DIR.mkdir(parents=True, exist_ok=True) self.stdout.write("Copy files into paperless...") diff --git a/src/documents/parsers.py b/src/documents/parsers.py index b791d8322..a2b4677e6 100644 --- a/src/documents/parsers.py +++ b/src/documents/parsers.py @@ -322,7 +322,7 @@ class DocumentParser(LoggingMixin): super().__init__() self.logging_group = logging_group self.settings = self.get_settings() - os.makedirs(settings.SCRATCH_DIR, exist_ok=True) + settings.SCRATCH_DIR.mkdir(parents=True, exist_ok=True) self.tempdir = Path( tempfile.mkdtemp(prefix="paperless-", dir=settings.SCRATCH_DIR), ) diff --git a/src/documents/tests/test_date_parsing.py b/src/documents/tests/test_date_parsing.py index d4ea71be5..4c75e5f6b 100644 --- a/src/documents/tests/test_date_parsing.py +++ b/src/documents/tests/test_date_parsing.py @@ -1,7 +1,4 @@ import datetime -import os -import shutil -from uuid import uuid4 from dateutil import tz from django.conf import settings @@ -13,17 +10,6 @@ from documents.parsers import parse_date_generator class TestDate(TestCase): - SAMPLE_FILES = os.path.join( - os.path.dirname(__file__), - "../../paperless_tesseract/tests/samples", - ) - SCRATCH = f"/tmp/paperless-tests-{str(uuid4())[:8]}" - - def setUp(self): - os.makedirs(self.SCRATCH, exist_ok=True) - - def tearDown(self): - shutil.rmtree(self.SCRATCH) def test_date_format_1(self): text = "lorem ipsum 130218 lorem ipsum" @@ -93,7 +79,6 @@ class TestDate(TestCase): datetime.datetime(2020, 3, 1, 0, 0, tzinfo=tz.gettz(settings.TIME_ZONE)), ) - @override_settings(SCRATCH_DIR=SCRATCH) def test_date_format_9(self): text = "lorem ipsum\n27. Nullmonth 2020\nMärz 2020\nlorem ipsum" self.assertEqual( diff --git a/src/documents/tests/test_file_handling.py b/src/documents/tests/test_file_handling.py index 225008b78..a924e377b 100644 --- a/src/documents/tests/test_file_handling.py +++ b/src/documents/tests/test_file_handling.py @@ -470,12 +470,12 @@ class TestFileHandling(DirectoriesMixin, FileSystemAssertsMixin, TestCase): def test_try_delete_empty_directories(self): # Create our working directory - tmp = os.path.join(settings.ORIGINALS_DIR, "test_delete_empty") - os.makedirs(tmp) + tmp: Path = settings.ORIGINALS_DIR / "test_delete_empty" + tmp.mkdir(exist_ok=True, parents=True) - os.makedirs(os.path.join(tmp, "notempty")) - Path(os.path.join(tmp, "notempty", "file")).touch() - os.makedirs(os.path.join(tmp, "notempty", "empty")) + (tmp / "notempty").mkdir(exist_ok=True, parents=True) + (tmp / "notempty" / "file").touch() + (tmp / "notempty" / "empty").mkdir(exist_ok=True, parents=True) delete_empty_directories( os.path.join(tmp, "notempty", "empty"), @@ -647,7 +647,7 @@ class TestFileHandlingWithArchive(DirectoriesMixin, FileSystemAssertsMixin, Test existing_archive_file = os.path.join(settings.ARCHIVE_DIR, "none", "my_doc.pdf") Path(original).touch() Path(archive).touch() - os.makedirs(os.path.join(settings.ARCHIVE_DIR, "none")) + (settings.ARCHIVE_DIR / "none").mkdir(parents=True, exist_ok=True) Path(existing_archive_file).touch() doc = Document.objects.create( mime_type="application/pdf", diff --git a/src/documents/views.py b/src/documents/views.py index 3be7f4ec6..c73a8050b 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -904,7 +904,7 @@ class PostDocumentView(GenericAPIView): t = int(mktime(datetime.now().timetuple())) - os.makedirs(settings.SCRATCH_DIR, exist_ok=True) + settings.SCRATCH_DIR.mkdir(parents=True, exist_ok=True) temp_file_path = Path(tempfile.mkdtemp(dir=settings.SCRATCH_DIR)) / Path( pathvalidate.sanitize_filename(doc_name), @@ -1136,7 +1136,7 @@ class BulkDownloadView(GenericAPIView): content = serializer.validated_data.get("content") follow_filename_format = serializer.validated_data.get("follow_formatting") - os.makedirs(settings.SCRATCH_DIR, exist_ok=True) + settings.SCRATCH_DIR.mkdir(parents=True, exist_ok=True) temp = tempfile.NamedTemporaryFile( dir=settings.SCRATCH_DIR, suffix="-compressed-archive", diff --git a/src/paperless/settings.py b/src/paperless/settings.py index a9792db9f..163492b1e 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -689,7 +689,7 @@ USE_TZ = True setup_logging_queues() -os.makedirs(LOGGING_DIR, exist_ok=True) +LOGGING_DIR.mkdir(parents=True, exist_ok=True) LOGROTATE_MAX_SIZE = os.getenv("PAPERLESS_LOGROTATE_MAX_SIZE", 1024 * 1024) LOGROTATE_MAX_BACKUPS = os.getenv("PAPERLESS_LOGROTATE_MAX_BACKUPS", 20) diff --git a/src/paperless_mail/mail.py b/src/paperless_mail/mail.py index 6514014ca..a4ff6478c 100644 --- a/src/paperless_mail/mail.py +++ b/src/paperless_mail/mail.py @@ -710,7 +710,7 @@ class MailAccountHandler(LoggingMixin): f"{message.subject} from {message.from_}", ) - os.makedirs(settings.SCRATCH_DIR, exist_ok=True) + settings.SCRATCH_DIR.mkdir(parents=True, exist_ok=True) temp_dir = Path( tempfile.mkdtemp( @@ -793,7 +793,7 @@ class MailAccountHandler(LoggingMixin): tag_ids, doc_type, ): - os.makedirs(settings.SCRATCH_DIR, exist_ok=True) + settings.SCRATCH_DIR.mkdir(parents=True, exist_ok=True) _, temp_filename = tempfile.mkstemp( prefix="paperless-mail-", dir=settings.SCRATCH_DIR,