From 0f92523d28850870f66f6997270397aed6dc6b76 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sat, 15 Apr 2023 21:39:44 -0700 Subject: [PATCH 1/2] Fix display of private objects sometimes --- src-ui/cypress/e2e/settings/settings.cy.ts | 13 ++++++++---- .../common/input/select/select.component.ts | 20 ++++++++++++++----- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src-ui/cypress/e2e/settings/settings.cy.ts b/src-ui/cypress/e2e/settings/settings.cy.ts index 259f15403..51a2ae380 100644 --- a/src-ui/cypress/e2e/settings/settings.cy.ts +++ b/src-ui/cypress/e2e/settings/settings.cy.ts @@ -56,8 +56,6 @@ describe('settings', () => { 'GET', 'http://localhost:8000/api/mail_accounts/*', (req) => { - console.log(req, this.newMailAccounts) - let response = { ...mailAccountsJson } if (this.newMailAccounts.length) { response.results = response.results.concat(this.newMailAccounts) @@ -142,7 +140,7 @@ describe('settings', () => { cy.get('app-saved-view-widget').contains('Inbox').should('not.exist') }) - it('should show a list of mail accounts & rules & support creation', () => { + it('should show a list of mail accounts & support creation', () => { cy.contains('a', 'Mail').click() cy.get('app-settings .tab-content ul li').its('length').should('eq', 5) // 2 headers, 2 accounts, 1 rule cy.contains('button', 'Add Account').click() @@ -162,6 +160,13 @@ describe('settings', () => { .wait('@getAccounts') cy.contains('Saved account') + cy.get('app-settings .tab-content ul li').its('length').should('eq', 6) + }) + + it('should show a list of mail rules & support creation', () => { + cy.contains('a', 'Mail').click() + cy.get('app-settings .tab-content ul li').its('length').should('eq', 5) // 2 headers, 2 accounts, 1 rule + cy.wait(1000) cy.contains('button', 'Add Rule').click() cy.contains('Create new mail rule') @@ -177,6 +182,6 @@ describe('settings', () => { .wait('@getRules') cy.contains('Saved rule').wait(1000) - cy.get('app-settings .tab-content ul li').its('length').should('eq', 7) + cy.get('app-settings .tab-content ul li').its('length').should('eq', 6) }) }) diff --git a/src-ui/src/app/components/common/input/select/select.component.ts b/src-ui/src/app/components/common/input/select/select.component.ts index ddf900bf6..5e5ee8100 100644 --- a/src-ui/src/app/components/common/input/select/select.component.ts +++ b/src-ui/src/app/components/common/input/select/select.component.ts @@ -27,21 +27,31 @@ export class SelectComponent extends AbstractInputComponent { } _items: any[] + privateItems: any[] = [] @Input() set items(items) { - if (this.value && items.find((i) => i.id === this.value) === undefined) { - items.push({ - id: this.value, + this._items = items + if (this.value) this.checkForPrivateItem(this.value) + } + + writeValue(newValue: any): void { + if (newValue && this._items) this.checkForPrivateItem(newValue) + super.writeValue(newValue) + } + + checkForPrivateItem(value) { + if (this._items.find((i) => i.id === value) === undefined) { + this.privateItems.push({ + id: value, name: $localize`Private`, private: true, }) } - this._items = items } get items(): any[] { - return this._items + return this._items?.concat(this.privateItems) } @Input() From 52e8a1aba38bb29f35630444fa77424bb0b56587 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sat, 15 Apr 2023 22:47:36 -0700 Subject: [PATCH 2/2] Include permissions for suggestions --- src/documents/matching.py | 41 +++++++++++++++++++++++++++++------- src/documents/permissions.py | 13 ++++++++++++ src/documents/views.py | 14 +++++++----- 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/src/documents/matching.py b/src/documents/matching.py index 63534ffe3..ad80ee0ad 100644 --- a/src/documents/matching.py +++ b/src/documents/matching.py @@ -6,6 +6,7 @@ from documents.models import DocumentType from documents.models import MatchingModel from documents.models import StoragePath from documents.models import Tag +from documents.permissions import get_objects_for_user_owner_aware logger = logging.getLogger("paperless.matching") @@ -19,40 +20,64 @@ def log_reason(matching_model, document, reason): ) -def match_correspondents(document, classifier): +def match_correspondents(document, classifier, user=None): pred_id = classifier.predict_correspondent(document.content) if classifier else None - correspondents = Correspondent.objects.all() + if user is not None: + correspondents = get_objects_for_user_owner_aware( + user, + "documents.view_correspondent", + Correspondent, + ) + else: + correspondents = Correspondent.objects.all() return list( filter(lambda o: matches(o, document) or o.pk == pred_id, correspondents), ) -def match_document_types(document, classifier): +def match_document_types(document, classifier, user=None): pred_id = classifier.predict_document_type(document.content) if classifier else None - document_types = DocumentType.objects.all() + if user is not None: + document_types = get_objects_for_user_owner_aware( + user, + "documents.view_documenttype", + DocumentType, + ) + else: + document_types = DocumentType.objects.all() return list( filter(lambda o: matches(o, document) or o.pk == pred_id, document_types), ) -def match_tags(document, classifier): +def match_tags(document, classifier, user=None): predicted_tag_ids = classifier.predict_tags(document.content) if classifier else [] - tags = Tag.objects.all() + if user is not None: + tags = get_objects_for_user_owner_aware(user, "documents.view_tag", Tag) + else: + tags = Tag.objects.all() return list( filter(lambda o: matches(o, document) or o.pk in predicted_tag_ids, tags), ) -def match_storage_paths(document, classifier): +def match_storage_paths(document, classifier, user=None): pred_id = classifier.predict_storage_path(document.content) if classifier else None - storage_paths = StoragePath.objects.all() + if user is not None: + storage_paths = get_objects_for_user_owner_aware( + user, + "documents.view_storagepath", + StoragePath, + ) + else: + storage_paths = StoragePath.objects.all() return list( filter( diff --git a/src/documents/permissions.py b/src/documents/permissions.py index 4af0ebae5..d4114e488 100644 --- a/src/documents/permissions.py +++ b/src/documents/permissions.py @@ -4,6 +4,7 @@ from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from guardian.models import GroupObjectPermission from guardian.shortcuts import assign_perm +from guardian.shortcuts import get_objects_for_user from guardian.shortcuts import get_users_with_perms from guardian.shortcuts import remove_perm from rest_framework.permissions import BasePermission @@ -101,3 +102,15 @@ def set_permissions_for_object(permissions, object): group, object, ) + + +def get_objects_for_user_owner_aware(user, perms, Model): + objects_owned = Model.objects.filter(owner=user) + objects_unowned = Model.objects.filter(owner__isnull=True) + objects_with_perms = get_objects_for_user( + user=user, + perms=perms, + klass=Model, + accept_global_perms=False, + ) + return objects_owned | objects_unowned | objects_with_perms diff --git a/src/documents/views.py b/src/documents/views.py index 597555be9..1edbdccc3 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -401,12 +401,16 @@ class DocumentViewSet( return Response( { - "correspondents": [c.id for c in match_correspondents(doc, classifier)], - "tags": [t.id for t in match_tags(doc, classifier)], - "document_types": [ - dt.id for dt in match_document_types(doc, classifier) + "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) ], - "storage_paths": [dt.id for dt in match_storage_paths(doc, classifier)], "dates": [ date.strftime("%Y-%m-%d") for date in dates if date is not None ],