From c3331086d55661f9f1a973194cef951d2ccd05d9 Mon Sep 17 00:00:00 2001
From: Michael Shamoon <4887959+shamoon@users.noreply.github.com>
Date: Tue, 8 Nov 2022 03:39:54 -0800
Subject: [PATCH] Basic data retrieval
---
.../manage/settings/settings.component.html | 31 +++++
.../manage/settings/settings.component.ts | 116 ++++++++++++++++--
src-ui/src/app/data/paperless-mail-account.ts | 23 ++++
src-ui/src/app/data/paperless-mail-rule.ts | 66 ++++++++++
.../app/services/rest/mail-account.service.ts | 52 ++++++++
.../app/services/rest/mail-rule.service.ts | 50 ++++++++
src/documents/serialisers.py | 60 +++++++++
src/documents/views.py | 38 ++++++
src/paperless/urls.py | 4 +
9 files changed, 431 insertions(+), 9 deletions(-)
create mode 100644 src-ui/src/app/data/paperless-mail-account.ts
create mode 100644 src-ui/src/app/data/paperless-mail-rule.ts
create mode 100644 src-ui/src/app/services/rest/mail-account.service.ts
create mode 100644 src-ui/src/app/services/rest/mail-rule.service.ts
diff --git a/src-ui/src/app/components/manage/settings/settings.component.html b/src-ui/src/app/components/manage/settings/settings.component.html
index 88bf34d36..9ba010c0e 100644
--- a/src-ui/src/app/components/manage/settings/settings.component.html
+++ b/src-ui/src/app/components/manage/settings/settings.component.html
@@ -216,6 +216,37 @@
+
+
+ Paperless Mail
+
+
+ Mail accounts
+
+
+
+
+
No mail accounts defined.
+
+
+ Mail rules
+
+
+
+
+
No mail rules defined.
+
+
+
+
diff --git a/src-ui/src/app/components/manage/settings/settings.component.ts b/src-ui/src/app/components/manage/settings/settings.component.ts
index 51a401e49..c6ffc13db 100644
--- a/src-ui/src/app/components/manage/settings/settings.component.ts
+++ b/src-ui/src/app/components/manage/settings/settings.component.ts
@@ -29,6 +29,10 @@ import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
import { ActivatedRoute } from '@angular/router'
import { ViewportScroller } from '@angular/common'
import { TourService } from 'ngx-ui-tour-ng-bootstrap'
+import { PaperlessMailAccount } from 'src/app/data/paperless-mail-account'
+import { PaperlessMailRule } from 'src/app/data/paperless-mail-rule'
+import { MailAccountService as MailAccountService } from 'src/app/services/rest/mail-account.service'
+import { MailRuleService } from 'src/app/services/rest/mail-rule.service'
@Component({
selector: 'app-settings',
@@ -40,6 +44,9 @@ export class SettingsComponent
{
savedViewGroup = new FormGroup({})
+ mailAccountGroup = new FormGroup({})
+ mailRuleGroup = new FormGroup({})
+
settingsForm = new FormGroup({
bulkEditConfirmationDialogs: new FormControl(null),
bulkEditApplyOnClose: new FormControl(null),
@@ -50,20 +57,28 @@ export class SettingsComponent
darkModeInvertThumbs: new FormControl(null),
themeColor: new FormControl(null),
useNativePdfViewer: new FormControl(null),
- savedViews: this.savedViewGroup,
displayLanguage: new FormControl(null),
dateLocale: new FormControl(null),
dateFormat: new FormControl(null),
+ commentsEnabled: new FormControl(null),
+ updateCheckingEnabled: new FormControl(null),
+
notificationsConsumerNewDocument: new FormControl(null),
notificationsConsumerSuccess: new FormControl(null),
notificationsConsumerFailed: new FormControl(null),
notificationsConsumerSuppressOnDashboard: new FormControl(null),
- commentsEnabled: new FormControl(null),
- updateCheckingEnabled: new FormControl(null),
+
+ savedViews: this.savedViewGroup,
+
+ mailAccounts: this.mailAccountGroup,
+ mailRules: this.mailRuleGroup,
})
savedViews: PaperlessSavedView[]
+ mailAccounts: PaperlessMailAccount[]
+ mailRules: PaperlessMailRule[]
+
store: BehaviorSubject
storeSub: Subscription
isDirty$: Observable
@@ -81,6 +96,8 @@ export class SettingsComponent
constructor(
public savedViewService: SavedViewService,
+ public mailAccountService: MailAccountService,
+ public mailRuleService: MailRuleService,
private documentListViewService: DocumentListViewService,
private toastService: ToastService,
private settings: SettingsService,
@@ -123,10 +140,13 @@ export class SettingsComponent
useNativePdfViewer: this.settings.get(
SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER
),
- savedViews: {},
displayLanguage: this.settings.getLanguage(),
dateLocale: this.settings.get(SETTINGS_KEYS.DATE_LOCALE),
dateFormat: this.settings.get(SETTINGS_KEYS.DATE_FORMAT),
+ commentsEnabled: this.settings.get(SETTINGS_KEYS.COMMENTS_ENABLED),
+ updateCheckingEnabled: this.settings.get(
+ SETTINGS_KEYS.UPDATE_CHECKING_ENABLED
+ ),
notificationsConsumerNewDocument: this.settings.get(
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT
),
@@ -139,17 +159,25 @@ export class SettingsComponent
notificationsConsumerSuppressOnDashboard: this.settings.get(
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD
),
- commentsEnabled: this.settings.get(SETTINGS_KEYS.COMMENTS_ENABLED),
- updateCheckingEnabled: this.settings.get(
- SETTINGS_KEYS.UPDATE_CHECKING_ENABLED
- ),
+ savedViews: {},
+ mailAccounts: {},
+ mailRules: {},
}
}
ngOnInit() {
this.savedViewService.listAll().subscribe((r) => {
this.savedViews = r.results
- this.initialize()
+
+ this.mailAccountService.listAll().subscribe((r) => {
+ this.mailAccounts = r.results
+
+ this.mailRuleService.listAll().subscribe((r) => {
+ this.mailRules = r.results
+
+ this.initialize()
+ })
+ })
})
}
@@ -176,6 +204,76 @@ export class SettingsComponent
)
}
+ for (let account of this.mailAccounts) {
+ storeData.mailAccounts[account.id.toString()] = {
+ id: account.id,
+ name: account.name,
+ imap_server: account.imap_server,
+ imap_port: account.imap_port,
+ imap_security: account.imap_security,
+ username: account.username,
+ password: account.password,
+ character_set: account.character_set,
+ }
+ this.mailAccountGroup.addControl(
+ account.id.toString(),
+ new FormGroup({
+ id: new FormControl(null),
+ name: new FormControl(null),
+ imap_server: new FormControl(null),
+ imap_port: new FormControl(null),
+ imap_security: new FormControl(null),
+ username: new FormControl(null),
+ password: new FormControl(null),
+ character_set: new FormControl(null),
+ })
+ )
+ }
+
+ for (let rule of this.mailRules) {
+ storeData.mailRules[rule.id.toString()] = {
+ name: rule.name,
+ order: rule.order,
+ account: rule.account,
+ folder: rule.folder,
+ filter_from: rule.filter_from,
+ filter_subject: rule.filter_subject,
+ filter_body: rule.filter_body,
+ filter_attachment_filename: rule.filter_attachment_filename,
+ maximum_age: rule.maximum_age,
+ attachment_type: rule.attachment_type,
+ action: rule.action,
+ action_parameter: rule.action_parameter,
+ assign_title_from: rule.assign_title_from,
+ assign_tags: rule.assign_tags,
+ assign_document_type: rule.assign_document_type,
+ assign_correspondent_from: rule.assign_correspondent_from,
+ assign_correspondent: rule.assign_correspondent,
+ }
+ this.mailRuleGroup.addControl(
+ rule.id.toString(),
+ new FormGroup({
+ name: new FormControl(null),
+ order: new FormControl(null),
+ account: new FormControl(null),
+ folder: new FormControl(null),
+ filter_from: new FormControl(null),
+ filter_subject: new FormControl(null),
+ filter_body: new FormControl(null),
+ filter_attachment_filename: new FormControl(null),
+ maximum_age: new FormControl(null),
+ attachment_type: new FormControl(null),
+ action: new FormControl(null),
+ action_parameter: new FormControl(null),
+ assign_title_from: new FormControl(null),
+ assign_tags: new FormControl(null),
+ assign_document_type: new FormControl(null),
+ assign_correspondent_from: new FormControl(null),
+ assign_correspondent: new FormControl(null),
+ })
+ )
+ }
+
this.store = new BehaviorSubject(storeData)
this.storeSub = this.store.asObservable().subscribe((state) => {
diff --git a/src-ui/src/app/data/paperless-mail-account.ts b/src-ui/src/app/data/paperless-mail-account.ts
new file mode 100644
index 000000000..243caa9bd
--- /dev/null
+++ b/src-ui/src/app/data/paperless-mail-account.ts
@@ -0,0 +1,23 @@
+import { ObjectWithId } from './object-with-id'
+
+export enum IMAPSecurity {
+ None = 0,
+ SSL = 1,
+ STARTTLS = 2,
+}
+
+export interface PaperlessMailAccount extends ObjectWithId {
+ name: string
+
+ imap_server: string
+
+ imap_port: number
+
+ imap_security: IMAPSecurity
+
+ username: string
+
+ password: string
+
+ character_set?: string
+}
diff --git a/src-ui/src/app/data/paperless-mail-rule.ts b/src-ui/src/app/data/paperless-mail-rule.ts
new file mode 100644
index 000000000..0b54619a6
--- /dev/null
+++ b/src-ui/src/app/data/paperless-mail-rule.ts
@@ -0,0 +1,66 @@
+import { ObjectWithId } from './object-with-id'
+import { PaperlessCorrespondent } from './paperless-correspondent'
+import { PaperlessDocumentType } from './paperless-document-type'
+import { PaperlessMailAccount } from './paperless-mail-account'
+import { PaperlessTag } from './paperless-tag'
+
+export enum MailFilterAttachmentType {
+ Attachments = 1,
+ Everything = 2,
+}
+
+export enum MailAction {
+ Delete = 1,
+ Move = 2,
+ MarkRead = 3,
+ Flag = 4,
+ Tag = 5,
+}
+
+export enum MailMetadataTitleOption {
+ FromSubject = 1,
+ FromFilename = 2,
+}
+
+export enum MailMetadataCorrespondentOption {
+ FromNothing = 1,
+ FromEmail = 2,
+ FromName = 3,
+ FromCustom = 4,
+}
+
+export interface PaperlessMailRule extends ObjectWithId {
+ name: string
+
+ order: number
+
+ account: PaperlessMailAccount
+
+ folder: string
+
+ filter_from: string
+
+ filter_subject: string
+
+ filter_body: string
+
+ filter_attachment_filename: string
+
+ maximum_age: number
+
+ attachment_type: MailFilterAttachmentType
+
+ action: MailAction
+
+ action_parameter?: string
+
+ assign_title_from: MailMetadataTitleOption
+
+ assign_tags?: PaperlessTag[]
+
+ assign_document_type?: PaperlessDocumentType
+
+ assign_correspondent_from?: MailMetadataCorrespondentOption
+
+ assign_correspondent?: PaperlessCorrespondent
+}
diff --git a/src-ui/src/app/services/rest/mail-account.service.ts b/src-ui/src/app/services/rest/mail-account.service.ts
new file mode 100644
index 000000000..a4db86684
--- /dev/null
+++ b/src-ui/src/app/services/rest/mail-account.service.ts
@@ -0,0 +1,52 @@
+import { HttpClient } from '@angular/common/http'
+import { Injectable } from '@angular/core'
+import { combineLatest, Observable } from 'rxjs'
+import { tap } from 'rxjs/operators'
+import { PaperlessMailAccount } from 'src/app/data/paperless-mail-account'
+import { AbstractPaperlessService } from './abstract-paperless-service'
+
+@Injectable({
+ providedIn: 'root',
+})
+export class MailAccountService extends AbstractPaperlessService {
+ loading: boolean
+
+ constructor(http: HttpClient) {
+ super(http, 'mail_accounts')
+ this.reload()
+ }
+
+ private reload() {
+ this.loading = true
+ this.listAll().subscribe((r) => {
+ this.mailAccounts = r.results
+ this.loading = false
+ })
+ }
+
+ private mailAccounts: PaperlessMailAccount[] = []
+
+ get allAccounts() {
+ return this.mailAccounts
+ }
+
+ create(o: PaperlessMailAccount) {
+ return super.create(o).pipe(tap(() => this.reload()))
+ }
+
+ update(o: PaperlessMailAccount) {
+ return super.update(o).pipe(tap(() => this.reload()))
+ }
+
+ patchMany(
+ objects: PaperlessMailAccount[]
+ ): Observable {
+ return combineLatest(objects.map((o) => super.patch(o))).pipe(
+ tap(() => this.reload())
+ )
+ }
+
+ delete(o: PaperlessMailAccount) {
+ return super.delete(o).pipe(tap(() => this.reload()))
+ }
+}
diff --git a/src-ui/src/app/services/rest/mail-rule.service.ts b/src-ui/src/app/services/rest/mail-rule.service.ts
new file mode 100644
index 000000000..4e7148496
--- /dev/null
+++ b/src-ui/src/app/services/rest/mail-rule.service.ts
@@ -0,0 +1,50 @@
+import { HttpClient } from '@angular/common/http'
+import { Injectable } from '@angular/core'
+import { combineLatest, Observable } from 'rxjs'
+import { tap } from 'rxjs/operators'
+import { PaperlessMailRule } from 'src/app/data/paperless-mail-rule'
+import { AbstractPaperlessService } from './abstract-paperless-service'
+
+@Injectable({
+ providedIn: 'root',
+})
+export class MailRuleService extends AbstractPaperlessService {
+ loading: boolean
+
+ constructor(http: HttpClient) {
+ super(http, 'mail_rules')
+ this.reload()
+ }
+
+ private reload() {
+ this.loading = true
+ this.listAll().subscribe((r) => {
+ this.mailRules = r.results
+ this.loading = false
+ })
+ }
+
+ private mailRules: PaperlessMailRule[] = []
+
+ get allRules() {
+ return this.mailRules
+ }
+
+ create(o: PaperlessMailRule) {
+ return super.create(o).pipe(tap(() => this.reload()))
+ }
+
+ update(o: PaperlessMailRule) {
+ return super.update(o).pipe(tap(() => this.reload()))
+ }
+
+ patchMany(objects: PaperlessMailRule[]): Observable {
+ return combineLatest(objects.map((o) => super.patch(o))).pipe(
+ tap(() => this.reload())
+ )
+ }
+
+ delete(o: PaperlessMailRule) {
+ return super.delete(o).pipe(tap(() => this.reload()))
+ }
+}
diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py
index db282cacd..86e0f4a12 100644
--- a/src/documents/serialisers.py
+++ b/src/documents/serialisers.py
@@ -28,6 +28,8 @@ from .models import UiSettings
from .models import PaperlessTask
from .parsers import is_mime_type_supported
+from paperless_mail.models import MailAccount, MailRule
+
# https://www.django-rest-framework.org/api-guide/serializers/#example
class DynamicFieldsModelSerializer(serializers.ModelSerializer):
@@ -688,3 +690,61 @@ class AcknowledgeTasksViewSerializer(serializers.Serializer):
def validate_tasks(self, tasks):
self._validate_task_id_list(tasks)
return tasks
+
+
+class MailAccountSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = MailAccount
+ depth = 1
+ fields = [
+ "id",
+ "name",
+ "imap_server",
+ "imap_port",
+ "imap_security",
+ "username",
+ "password",
+ "character_set",
+ ]
+
+ def update(self, instance, validated_data):
+ super().update(instance, validated_data)
+ return instance
+
+ def create(self, validated_data):
+ mail_account = MailAccount.objects.create(**validated_data)
+ return mail_account
+
+
+class MailRuleSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = MailRule
+ depth = 1
+ fields = [
+ "id",
+ "name",
+ "account",
+ "folder",
+ "filter_from",
+ "filter_subject",
+ "filter_body",
+ "filter_attachment_filename",
+ "maximum_age",
+ "action",
+ "action_parameter",
+ "assign_title_from",
+ "assign_tags",
+ "assign_correspondent_from",
+ "assign_correspondent",
+ "assign_document_type",
+ "order",
+ "attachment_type",
+ ]
+
+ def update(self, instance, validated_data):
+ super().update(instance, validated_data)
+ return instance
+
+ def create(self, validated_data):
+ mail_rule = MailRule.objects.create(**validated_data)
+ return mail_rule
diff --git a/src/documents/views.py b/src/documents/views.py
index 10225be6f..f980805f2 100644
--- a/src/documents/views.py
+++ b/src/documents/views.py
@@ -33,6 +33,8 @@ from packaging import version as packaging_version
from paperless import version
from paperless.db import GnuPG
from paperless.views import StandardPagination
+from paperless_mail.models import MailAccount
+from paperless_mail.models import MailRule
from rest_framework import parsers
from rest_framework.decorators import action
from rest_framework.exceptions import NotFound
@@ -81,6 +83,8 @@ from .serialisers import CorrespondentSerializer
from .serialisers import DocumentListSerializer
from .serialisers import DocumentSerializer
from .serialisers import DocumentTypeSerializer
+from .serialisers import MailAccountSerializer
+from .serialisers import MailRuleSerializer
from .serialisers import PostDocumentSerializer
from .serialisers import SavedViewSerializer
from .serialisers import StoragePathSerializer
@@ -910,3 +914,37 @@ class AcknowledgeTasksView(GenericAPIView):
return Response({"result": result})
except Exception:
return HttpResponseBadRequest()
+
+
+class MailAccountViewSet(ModelViewSet):
+ model = MailAccount
+
+ queryset = MailAccount.objects.all()
+ serializer_class = MailAccountSerializer
+ pagination_class = StandardPagination
+ permission_classes = (IsAuthenticated,)
+
+ # TODO: user-scoped
+ # def get_queryset(self):
+ # user = self.request.user
+ # return MailAccount.objects.filter(user=user)
+
+ # def perform_create(self, serializer):
+ # serializer.save(user=self.request.user)
+
+
+class MailRuleViewSet(ModelViewSet):
+ model = MailRule
+
+ queryset = MailRule.objects.all()
+ serializer_class = MailRuleSerializer
+ pagination_class = StandardPagination
+ permission_classes = (IsAuthenticated,)
+
+ # TODO: user-scoped
+ # def get_queryset(self):
+ # user = self.request.user
+ # return MailRule.objects.filter(user=user)
+
+ # def perform_create(self, serializer):
+ # serializer.save(user=self.request.user)
diff --git a/src/paperless/urls.py b/src/paperless/urls.py
index 46309e1e6..afad7cb9f 100644
--- a/src/paperless/urls.py
+++ b/src/paperless/urls.py
@@ -14,6 +14,8 @@ from documents.views import CorrespondentViewSet
from documents.views import DocumentTypeViewSet
from documents.views import IndexView
from documents.views import LogViewSet
+from documents.views import MailAccountViewSet
+from documents.views import MailRuleViewSet
from documents.views import PostDocumentView
from documents.views import RemoteVersionView
from documents.views import SavedViewViewSet
@@ -39,6 +41,8 @@ api_router.register(r"tags", TagViewSet)
api_router.register(r"saved_views", SavedViewViewSet)
api_router.register(r"storage_paths", StoragePathViewSet)
api_router.register(r"tasks", TasksViewSet, basename="tasks")
+api_router.register(r"mail_accounts", MailAccountViewSet)
+api_router.register(r"mail_rules", MailRuleViewSet)
urlpatterns = [