diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index c64cef921..e0d30bdc2 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -560,7 +560,7 @@ src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 72 + 75 src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.html @@ -726,7 +726,7 @@ src/app/components/manage/mail/mail.component.html - 137 + 146 src/app/components/manage/management-list/management-list.component.html @@ -1100,11 +1100,11 @@ src/app/components/manage/mail/mail.component.html - 99 + 108 src/app/components/manage/mail/mail.component.html - 111 + 120 src/app/components/manage/management-list/management-list.component.html @@ -1406,7 +1406,7 @@ src/app/components/manage/mail/mail.component.html - 81 + 82 src/app/components/manage/management-list/management-list.component.html @@ -1505,11 +1505,11 @@ src/app/components/manage/mail/mail.component.html - 100 + 109 src/app/components/manage/mail/mail.component.html - 114 + 123 src/app/components/manage/management-list/management-list.component.html @@ -1668,7 +1668,7 @@ src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 71 + 74 src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.html @@ -2227,7 +2227,7 @@ src/app/components/manage/mail/mail.component.ts - 179 + 194 src/app/components/manage/management-list/management-list.component.ts @@ -2430,11 +2430,11 @@ src/app/components/manage/mail/mail.component.html - 98 + 107 src/app/components/manage/mail/mail.component.html - 108 + 117 src/app/components/manage/management-list/management-list.component.html @@ -2578,7 +2578,7 @@ src/app/components/manage/mail/mail.component.ts - 181 + 196 src/app/components/manage/management-list/management-list.component.ts @@ -3666,166 +3666,185 @@ 88 - - Rule order - - src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 16 - - Account src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 19 + 16 src/app/components/manage/mail/mail.component.html 80 + + Order + + src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html + 19 + + + + Enabled + + src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html + 22 + + + src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html + 19 + + + src/app/components/manage/mail/mail.component.html + 96 + + + src/app/components/manage/workflows/workflows.component.html + 30 + + Paperless will only process mails that match all of the criteria specified below. src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 24 + 27 Folder src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 26 + 29 Subfolders must be separated by a delimiter, often a dot ('.') or slash ('/'), but it varies by mail server. src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 26 + 29 Maximum age (days) src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 27 + 30 Filter from src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 30 + 33 Filter to src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 31 + 34 Filter subject src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 32 + 35 Filter body src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 33 + 36 Consumption scope src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 39 + 42 See docs for .eml processing requirements src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 39 + 42 Attachment type src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 40 + 43 Include only files matching src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 43 + 46 Optional. Wildcards e.g. *.pdf or *invoice* allowed. Can be comma-separated list. Case insensitive. src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 43 + 46 src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 44 + 47 Exclude files matching src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 44 + 47 Action src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 50 + 53 Only performed if the mail is processed. src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 50 + 53 Action parameter src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 52 + 55 Assign title from src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 54 + 57 Assign owner from rule src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 55 + 58 Assign document type src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 59 + 62 src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html @@ -3836,14 +3855,14 @@ Assign correspondent from src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 60 + 63 Assign correspondent src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 62 + 65 src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html @@ -3854,7 +3873,7 @@ Error src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html - 69 + 72 src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html @@ -4166,17 +4185,6 @@ 18 - - Enabled - - src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 19 - - - src/app/components/manage/workflows/workflows.component.html - 30 - - Triggers @@ -5043,11 +5051,11 @@ src/app/components/manage/mail/mail.component.html - 101 + 110 src/app/components/manage/mail/mail.component.html - 119 + 128 src/app/components/manage/workflows/workflows.component.html @@ -5358,6 +5366,10 @@ src/app/components/common/toasts/toasts.component.html 26 + + src/app/components/manage/mail/mail.component.html + 81 + src/app/components/manage/workflows/workflows.component.html 19 @@ -7513,11 +7525,22 @@ 79 + + Disabled + + src/app/components/manage/mail/mail.component.html + 96 + + + src/app/components/manage/workflows/workflows.component.html + 30 + + No mail rules defined. src/app/components/manage/mail/mail.component.html - 128 + 137 @@ -7590,46 +7613,67 @@ 162 + + Rule "" enabled. + + src/app/components/manage/mail/mail.component.ts + 178 + + + + Rule "" disabled. + + src/app/components/manage/mail/mail.component.ts + 179 + + + + Error toggling rule. + + src/app/components/manage/mail/mail.component.ts + 183 + + Confirm delete mail rule src/app/components/manage/mail/mail.component.ts - 177 + 192 This operation will permanently delete this mail rule. src/app/components/manage/mail/mail.component.ts - 178 + 193 Deleted mail rule src/app/components/manage/mail/mail.component.ts - 187 + 202 Error deleting mail rule. src/app/components/manage/mail/mail.component.ts - 196 + 211 Permissions updated src/app/components/manage/mail/mail.component.ts - 218 + 233 Error updating permissions src/app/components/manage/mail/mail.component.ts - 223 + 238 src/app/components/manage/management-list/management-list.component.ts @@ -7879,13 +7923,6 @@ 9 - - Disabled - - src/app/components/manage/workflows/workflows.component.html - 30 - - No workflows defined. @@ -8779,21 +8816,21 @@ Successfully completed one-time migratration of settings to the database! src/app/services/settings.service.ts - 573 + 574 Unable to migrate settings to the database, please try saving manually. src/app/services/settings.service.ts - 574 + 575 You can restart the tour from the settings page. src/app/services/settings.service.ts - 644 + 645 diff --git a/src-ui/src/app/components/app-frame/global-search/global-search.component.scss b/src-ui/src/app/components/app-frame/global-search/global-search.component.scss index 1b4f6cd19..bbb7840c5 100644 --- a/src-ui/src/app/components/app-frame/global-search/global-search.component.scss +++ b/src-ui/src/app/components/app-frame/global-search/global-search.component.scss @@ -65,10 +65,6 @@ form { --pngx-focus-alpha: 0; } -.cursor-pointer { - cursor: pointer; -} - .mh-75 { max-height: 75vh; } diff --git a/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html b/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html index a7c0617b0..a9ad3040b 100644 --- a/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html +++ b/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html @@ -12,12 +12,15 @@
-
- -
-
+
+
+ +
+
+ +

diff --git a/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.spec.ts index 53546a55d..19655ae4d 100644 --- a/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.spec.ts @@ -24,6 +24,7 @@ import { TextComponent } from '../../input/text/text.component' import { EditDialogMode } from '../edit-dialog.component' import { MailRuleEditDialogComponent } from './mail-rule-edit-dialog.component' import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' +import { SwitchComponent } from '../../input/switch/switch.component' describe('MailRuleEditDialogComponent', () => { let component: MailRuleEditDialogComponent @@ -43,6 +44,7 @@ describe('MailRuleEditDialogComponent', () => { TagsComponent, SafeHtmlPipe, CheckComponent, + SwitchComponent, ], imports: [FormsModule, ReactiveFormsModule, NgSelectModule, NgbModule], providers: [ diff --git a/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts index 51793e78a..633c49967 100644 --- a/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts +++ b/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts @@ -153,6 +153,7 @@ export class MailRuleEditDialogComponent extends EditDialogComponent { return new FormGroup({ name: new FormControl(null), account: new FormControl(null), + enabled: new FormControl(true), folder: new FormControl('INBOX'), filter_from: new FormControl(null), filter_to: new FormControl(null), diff --git a/src-ui/src/app/components/document-list/document-list.component.scss b/src-ui/src/app/components/document-list/document-list.component.scss index fb7db4b01..0e10b83da 100644 --- a/src-ui/src/app/components/document-list/document-list.component.scss +++ b/src-ui/src/app/components/document-list/document-list.component.scss @@ -6,10 +6,6 @@ tr { user-select: none; } -.cursor-pointer { - cursor: pointer; -} - .table-row-selected { background-color: var(--pngx-primary-faded); } 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 add5614c4..296d80055 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.html +++ b/src-ui/src/app/components/manage/mail/mail.component.html @@ -78,6 +78,7 @@
Name
Sort Order
Account
+
Status
Actions
@@ -86,8 +87,16 @@
  • -
    {{rule.order}}
    +
    {{rule.order}}
    {{(mailAccountService.getCached(rule.account) | async)?.name}}
    +
    +
    + + +
    +
    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 4a134b880..14cd10944 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 @@ -43,14 +43,15 @@ import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' import { SwitchComponent } from '../../common/input/switch/switch.component' import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' +import { By } from '@angular/platform-browser' const mailAccounts = [ { id: 1, name: 'account1' }, { id: 2, name: 'account2' }, ] const mailRules = [ - { id: 1, name: 'rule1', owner: 1, account: 1 }, - { id: 2, name: 'rule2', owner: 2, account: 2 }, + { id: 1, name: 'rule1', owner: 1, account: 1, enabled: true }, + { id: 2, name: 'rule2', owner: 2, account: 2, enabled: true }, ] describe('MailComponent', () => { @@ -321,4 +322,30 @@ describe('MailComponent', () => { dialog.confirmClicked.emit({ permissions: perms, merge: true }) expect(accountPatchSpy).toHaveBeenCalled() }) + + it('should update mail rule when enable is toggled', () => { + completeSetup() + const patchSpy = jest.spyOn(mailRuleService, 'patch') + const toggleInput = fixture.debugElement.query( + By.css('input[type="checkbox"]') + ) + const toastErrorSpy = jest.spyOn(toastService, 'showError') + const toastInfoSpy = jest.spyOn(toastService, 'showInfo') + // fail first + patchSpy.mockReturnValueOnce( + throwError(() => new Error('Error getting config')) + ) + toggleInput.nativeElement.click() + expect(patchSpy).toHaveBeenCalled() + expect(toastErrorSpy).toHaveBeenCalled() + // succeed second + patchSpy.mockReturnValueOnce(of(mailRules[0] as MailRule)) + toggleInput.nativeElement.click() + patchSpy.mockReturnValueOnce( + of({ ...mailRules[0], enabled: false } as MailRule) + ) + toggleInput.nativeElement.click() + expect(patchSpy).toHaveBeenCalled() + expect(toastInfoSpy).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 5d00b6c13..288e8e121 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.ts +++ b/src-ui/src/app/components/manage/mail/mail.component.ts @@ -170,6 +170,21 @@ export class MailComponent this.editMailRule(clone, true) } + onMailRuleEnableToggled(rule: MailRule) { + this.mailRuleService.patch(rule).subscribe({ + next: () => { + this.toastService.showInfo( + rule.enabled + ? $localize`Rule "${rule.name}" enabled.` + : $localize`Rule "${rule.name}" disabled.` + ) + }, + error: (e) => { + this.toastService.showError($localize`Error toggling rule.`, e) + }, + }) + } + deleteMailRule(rule: MailRule) { const modal = this.modalService.open(ConfirmDialogComponent, { backdrop: 'static', diff --git a/src-ui/src/app/data/mail-rule.ts b/src-ui/src/app/data/mail-rule.ts index 2611fa3ba..7888b19e6 100644 --- a/src-ui/src/app/data/mail-rule.ts +++ b/src-ui/src/app/data/mail-rule.ts @@ -39,6 +39,8 @@ export interface MailRule extends ObjectWithPermissions { order: number + enabled: boolean + folder: string filter_from: string diff --git a/src-ui/src/app/services/rest/mail-rule.service.spec.ts b/src-ui/src/app/services/rest/mail-rule.service.spec.ts index ea84e8b86..87e21172c 100644 --- a/src-ui/src/app/services/rest/mail-rule.service.spec.ts +++ b/src-ui/src/app/services/rest/mail-rule.service.spec.ts @@ -18,6 +18,7 @@ const mail_rules = [ id: 1, account: 1, order: 1, + enabled: true, folder: 'INBOX', filter_from: null, filter_to: null, @@ -36,6 +37,7 @@ const mail_rules = [ id: 2, account: 1, order: 1, + enabled: true, folder: 'INBOX', filter_from: null, filter_to: null, @@ -54,6 +56,7 @@ const mail_rules = [ id: 3, account: 1, order: 1, + enabled: true, folder: 'INBOX', filter_from: null, filter_to: null, diff --git a/src-ui/src/styles.scss b/src-ui/src/styles.scss index c83ebd493..ef856fbc7 100644 --- a/src-ui/src/styles.scss +++ b/src-ui/src/styles.scss @@ -369,6 +369,10 @@ textarea, cursor: not-allowed; } +.cursor-pointer { + cursor: pointer; +} + ul.pagination { margin-bottom: 0; } diff --git a/src/documents/tests/test_migration_workflows.py b/src/documents/tests/test_migration_workflows.py index 403067ca6..81bb577b2 100644 --- a/src/documents/tests/test_migration_workflows.py +++ b/src/documents/tests/test_migration_workflows.py @@ -8,7 +8,7 @@ class TestMigrateWorkflow(TestMigrations): dependencies = ( ( "paperless_mail", - "0025_alter_mailaccount_owner_alter_mailrule_owner_and_more", + "0026_mailrule_enabled", ), ) diff --git a/src/paperless_mail/admin.py b/src/paperless_mail/admin.py index adec5e17c..2ff313584 100644 --- a/src/paperless_mail/admin.py +++ b/src/paperless_mail/admin.py @@ -53,7 +53,7 @@ class MailRuleAdmin(GuardedModelAdmin): } fieldsets = ( - (None, {"fields": ("name", "order", "account", "folder")}), + (None, {"fields": ("name", "order", "account", "enabled", "folder")}), ( _("Filter"), { diff --git a/src/paperless_mail/mail.py b/src/paperless_mail/mail.py index b52a2ebe4..84f97b742 100644 --- a/src/paperless_mail/mail.py +++ b/src/paperless_mail/mail.py @@ -536,6 +536,9 @@ class MailAccountHandler(LoggingMixin): ) for rule in account.rules.order_by("order"): + if not rule.enabled: + self.log.debug(f"Rule {rule}: Skipping disabled rule") + continue try: total_processed_files += self._handle_mail_rule( M, diff --git a/src/paperless_mail/migrations/0026_mailrule_enabled.py b/src/paperless_mail/migrations/0026_mailrule_enabled.py new file mode 100644 index 000000000..c10ee698c --- /dev/null +++ b/src/paperless_mail/migrations/0026_mailrule_enabled.py @@ -0,0 +1,21 @@ +# Generated by Django 5.1.1 on 2024-09-30 15:17 + +from django.db import migrations +from django.db import models + + +class Migration(migrations.Migration): + dependencies = [ + ( + "paperless_mail", + "0025_alter_mailaccount_owner_alter_mailrule_owner_and_more", + ), + ] + + operations = [ + migrations.AddField( + model_name="mailrule", + name="enabled", + field=models.BooleanField(default=True, verbose_name="enabled"), + ), + ] diff --git a/src/paperless_mail/models.py b/src/paperless_mail/models.py index c53b16f1f..c23ea48c7 100644 --- a/src/paperless_mail/models.py +++ b/src/paperless_mail/models.py @@ -115,6 +115,8 @@ class MailRule(document_models.ModelWithOwner): verbose_name=_("account"), ) + enabled = models.BooleanField(_("enabled"), default=True) + folder = models.CharField( _("folder"), default="INBOX", diff --git a/src/paperless_mail/serialisers.py b/src/paperless_mail/serialisers.py index 38ee9661e..9237b47de 100644 --- a/src/paperless_mail/serialisers.py +++ b/src/paperless_mail/serialisers.py @@ -74,6 +74,7 @@ class MailRuleSerializer(OwnedObjectSerializer): "id", "name", "account", + "enabled", "folder", "filter_from", "filter_to", diff --git a/src/paperless_mail/tests/test_mail.py b/src/paperless_mail/tests/test_mail.py index c12b54ffe..9078335a6 100644 --- a/src/paperless_mail/tests/test_mail.py +++ b/src/paperless_mail/tests/test_mail.py @@ -1388,6 +1388,41 @@ class TestMail( self.assertEqual(len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", False)), 0) self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) + def test_disabled_rule(self): + """ + GIVEN: + - Mail rule is disabled + WHEN: + - Mail account is handled + THEN: + - Should not process any messages + """ + account = MailAccount.objects.create( + name="test", + imap_server="", + username="admin", + password="secret", + ) + MailRule.objects.create( + name="testrule", + account=account, + action=MailRule.MailAction.MARK_READ, + enabled=False, + ) + + self.mail_account_handler.handle_mail_account(account) + self.mailMocker.apply_mail_actions() + + self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 3) + self.assertEqual(len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", False)), 2) + + self.mail_account_handler.handle_mail_account(account) + self.mailMocker.apply_mail_actions() + self.assertEqual( + len(self.mailMocker.bogus_mailbox.fetch("UNSEEN", False)), + 2, + ) # still 2 + class TestManagementCommand(TestCase): @mock.patch(