mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-05-01 11:19:32 -05:00
Messing around with notification action to start
This commit is contained in:
parent
cc93bc41df
commit
c4d59e0e77
@ -322,6 +322,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@case (WorkflowActionType.Notification) {
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<pngx-input-text i18n-title title="Notification subject" formControlName="notification_subject" [error]="error?.actions?.[i]?.notification_subject"></pngx-input-text>
|
||||||
|
<pngx-input-text i18n-title title="Notification body" formControlName="notification_body" [error]="error?.actions?.[i]?.notification_body"></pngx-input-text>
|
||||||
|
<pngx-input-text i18n-title title="Notification emails" formControlName="notification_destination_emails" [error]="error?.actions?.[i]?.notification_destination_emails"></pngx-input-text>
|
||||||
|
<pngx-input-text i18n-title title="Notification url" formControlName="notification_destination_url" [error]="error?.actions?.[i]?.notification_destination_url"></pngx-input-text>
|
||||||
|
<pngx-input-switch i18n-title title="Notification include document" formControlName="notification_include_document"></pngx-input-switch>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -96,6 +96,10 @@ export const WORKFLOW_ACTION_OPTIONS = [
|
|||||||
id: WorkflowActionType.Removal,
|
id: WorkflowActionType.Removal,
|
||||||
name: $localize`Removal`,
|
name: $localize`Removal`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: WorkflowActionType.Notification,
|
||||||
|
name: $localize`Notification`,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const TRIGGER_MATCHING_ALGORITHMS = MATCHING_ALGORITHMS.filter(
|
const TRIGGER_MATCHING_ALGORITHMS = MATCHING_ALGORITHMS.filter(
|
||||||
@ -402,6 +406,17 @@ export class WorkflowEditDialogComponent
|
|||||||
remove_all_custom_fields: new FormControl(
|
remove_all_custom_fields: new FormControl(
|
||||||
action.remove_all_custom_fields
|
action.remove_all_custom_fields
|
||||||
),
|
),
|
||||||
|
notification_subject: new FormControl(action.notification_subject),
|
||||||
|
notification_body: new FormControl(action.notification_body),
|
||||||
|
notification_destination_emails: new FormControl(
|
||||||
|
action.notification_destination_emails
|
||||||
|
),
|
||||||
|
notification_destination_url: new FormControl(
|
||||||
|
action.notification_destination_url
|
||||||
|
),
|
||||||
|
notification_include_document: new FormControl(
|
||||||
|
action.notification_include_document
|
||||||
|
),
|
||||||
}),
|
}),
|
||||||
{ emitEvent }
|
{ emitEvent }
|
||||||
)
|
)
|
||||||
@ -503,6 +518,11 @@ export class WorkflowEditDialogComponent
|
|||||||
remove_all_permissions: false,
|
remove_all_permissions: false,
|
||||||
remove_custom_fields: [],
|
remove_custom_fields: [],
|
||||||
remove_all_custom_fields: false,
|
remove_all_custom_fields: false,
|
||||||
|
notification_subject: null,
|
||||||
|
notification_body: null,
|
||||||
|
notification_destination_emails: null,
|
||||||
|
notification_destination_url: null,
|
||||||
|
notification_include_document: null,
|
||||||
}
|
}
|
||||||
this.object.actions.push(action)
|
this.object.actions.push(action)
|
||||||
this.createActionField(action)
|
this.createActionField(action)
|
||||||
|
@ -3,6 +3,7 @@ import { ObjectWithId } from './object-with-id'
|
|||||||
export enum WorkflowActionType {
|
export enum WorkflowActionType {
|
||||||
Assignment = 1,
|
Assignment = 1,
|
||||||
Removal = 2,
|
Removal = 2,
|
||||||
|
Notification = 3,
|
||||||
}
|
}
|
||||||
export interface WorkflowAction extends ObjectWithId {
|
export interface WorkflowAction extends ObjectWithId {
|
||||||
type: WorkflowActionType
|
type: WorkflowActionType
|
||||||
@ -62,4 +63,14 @@ export interface WorkflowAction extends ObjectWithId {
|
|||||||
remove_custom_fields?: number[] // [CustomField.id]
|
remove_custom_fields?: number[] // [CustomField.id]
|
||||||
|
|
||||||
remove_all_custom_fields?: boolean
|
remove_all_custom_fields?: boolean
|
||||||
|
|
||||||
|
notification_subject?: string
|
||||||
|
|
||||||
|
notification_body?: string
|
||||||
|
|
||||||
|
notification_destination_emails?: string
|
||||||
|
|
||||||
|
notification_destination_url?: string
|
||||||
|
|
||||||
|
notification_include_document?: boolean
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,7 @@ def settings(request):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"EMAIL_ENABLED": django_settings.EMAIL_HOST != "localhost"
|
"EMAIL_ENABLED": django_settings.EMAIL_ENABLED,
|
||||||
or django_settings.EMAIL_HOST_USER != "",
|
|
||||||
"DISABLE_REGULAR_LOGIN": django_settings.DISABLE_REGULAR_LOGIN,
|
"DISABLE_REGULAR_LOGIN": django_settings.DISABLE_REGULAR_LOGIN,
|
||||||
"REDIRECT_LOGIN_TO_SSO": django_settings.REDIRECT_LOGIN_TO_SSO,
|
"REDIRECT_LOGIN_TO_SSO": django_settings.REDIRECT_LOGIN_TO_SSO,
|
||||||
"ACCOUNT_ALLOW_SIGNUPS": django_settings.ACCOUNT_ALLOW_SIGNUPS,
|
"ACCOUNT_ALLOW_SIGNUPS": django_settings.ACCOUNT_ALLOW_SIGNUPS,
|
||||||
|
@ -0,0 +1,71 @@
|
|||||||
|
# Generated by Django 5.1.1 on 2024-10-23 17:15
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("documents", "1055_alter_storagepath_path"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="workflowaction",
|
||||||
|
name="notification_body",
|
||||||
|
field=models.TextField(
|
||||||
|
blank=True,
|
||||||
|
help_text="The body (message) of the notification, can include some placeholders, see documentation.",
|
||||||
|
null=True,
|
||||||
|
verbose_name="notification body",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="workflowaction",
|
||||||
|
name="notification_destination_emails",
|
||||||
|
field=models.TextField(
|
||||||
|
blank=True,
|
||||||
|
help_text="The destination email addresses for the notification, comma separated.",
|
||||||
|
null=True,
|
||||||
|
verbose_name="notification destination emails",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="workflowaction",
|
||||||
|
name="notification_destination_url",
|
||||||
|
field=models.URLField(
|
||||||
|
blank=True,
|
||||||
|
help_text="The destination URL for the notification.",
|
||||||
|
null=True,
|
||||||
|
verbose_name="notification destination url",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="workflowaction",
|
||||||
|
name="notification_include_document",
|
||||||
|
field=models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
verbose_name="include document in notification",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="workflowaction",
|
||||||
|
name="notification_subject",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True,
|
||||||
|
help_text="The subject of the notification, can include some placeholders, see documentation.",
|
||||||
|
max_length=256,
|
||||||
|
null=True,
|
||||||
|
verbose_name="notification subject",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="workflowaction",
|
||||||
|
name="type",
|
||||||
|
field=models.PositiveIntegerField(
|
||||||
|
choices=[(1, "Assignment"), (2, "Removal"), (3, "Notification")],
|
||||||
|
default=1,
|
||||||
|
verbose_name="Workflow Action Type",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
@ -1166,6 +1166,10 @@ class WorkflowAction(models.Model):
|
|||||||
2,
|
2,
|
||||||
_("Removal"),
|
_("Removal"),
|
||||||
)
|
)
|
||||||
|
NOTIFICATION = (
|
||||||
|
3,
|
||||||
|
_("Notification"),
|
||||||
|
)
|
||||||
|
|
||||||
type = models.PositiveIntegerField(
|
type = models.PositiveIntegerField(
|
||||||
_("Workflow Action Type"),
|
_("Workflow Action Type"),
|
||||||
@ -1367,6 +1371,48 @@ class WorkflowAction(models.Model):
|
|||||||
verbose_name=_("remove all custom fields"),
|
verbose_name=_("remove all custom fields"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
notification_subject = models.CharField(
|
||||||
|
_("notification subject"),
|
||||||
|
max_length=256,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
help_text=_(
|
||||||
|
"The subject of the notification, can include some placeholders, "
|
||||||
|
"see documentation.",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
notification_body = models.TextField(
|
||||||
|
_("notification body"),
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
help_text=_(
|
||||||
|
"The body (message) of the notification, can include some placeholders, "
|
||||||
|
"see documentation.",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
notification_destination_emails = models.TextField(
|
||||||
|
_("notification destination emails"),
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
help_text=_(
|
||||||
|
"The destination email addresses for the notification, comma separated.",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
notification_destination_url = models.URLField(
|
||||||
|
_("notification destination url"),
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
help_text=_("The destination URL for the notification."),
|
||||||
|
)
|
||||||
|
|
||||||
|
notification_include_document = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
verbose_name=_("include document in notification"),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("workflow action")
|
verbose_name = _("workflow action")
|
||||||
verbose_name_plural = _("workflow actions")
|
verbose_name_plural = _("workflow actions")
|
||||||
|
@ -1847,6 +1847,11 @@ class WorkflowActionSerializer(serializers.ModelSerializer):
|
|||||||
"remove_view_groups",
|
"remove_view_groups",
|
||||||
"remove_change_users",
|
"remove_change_users",
|
||||||
"remove_change_groups",
|
"remove_change_groups",
|
||||||
|
"notification_subject",
|
||||||
|
"notification_body",
|
||||||
|
"notification_destination_emails",
|
||||||
|
"notification_destination_url",
|
||||||
|
"notification_include_document",
|
||||||
]
|
]
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
@ -1884,6 +1889,38 @@ class WorkflowActionSerializer(serializers.ModelSerializer):
|
|||||||
{"assign_title": f'Invalid f-string detected: "{e.args[0]}"'},
|
{"assign_title": f'Invalid f-string detected: "{e.args[0]}"'},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
"notification_subject" in attrs
|
||||||
|
and attrs["notification_subject"] is not None
|
||||||
|
and len(attrs["notification_subject"]) > 0
|
||||||
|
and not (
|
||||||
|
attrs["notification_destination_emails"]
|
||||||
|
or attrs["notification_destination_url"]
|
||||||
|
)
|
||||||
|
):
|
||||||
|
raise serializers.ValidationError(
|
||||||
|
"Notification subject requires destination emails or URL",
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
(
|
||||||
|
(
|
||||||
|
"notification_destination_emails" in attrs
|
||||||
|
and attrs["notification_destination_emails"] is not None
|
||||||
|
and len(attrs["notification_destination_emails"]) > 0
|
||||||
|
)
|
||||||
|
or (
|
||||||
|
"notification_destination_url" in attrs
|
||||||
|
and attrs["notification_destination_url"] is not None
|
||||||
|
and len(attrs["notification_destination_url"]) > 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
and not attrs["notification_subject"]
|
||||||
|
and not attrs["notification_body"]
|
||||||
|
):
|
||||||
|
raise serializers.ValidationError(
|
||||||
|
"Notification subject and body required",
|
||||||
|
)
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
|
import httpx
|
||||||
from celery import states
|
from celery import states
|
||||||
from celery.signals import before_task_publish
|
from celery.signals import before_task_publish
|
||||||
from celery.signals import task_failure
|
from celery.signals import task_failure
|
||||||
@ -12,6 +13,7 @@ from django.contrib.admin.models import ADDITION
|
|||||||
from django.contrib.admin.models import LogEntry
|
from django.contrib.admin.models import LogEntry
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.core.mail import EmailMessage
|
||||||
from django.db import DatabaseError
|
from django.db import DatabaseError
|
||||||
from django.db import close_old_connections
|
from django.db import close_old_connections
|
||||||
from django.db import models
|
from django.db import models
|
||||||
@ -866,6 +868,68 @@ def run_workflows(
|
|||||||
):
|
):
|
||||||
overrides.custom_field_ids.remove(field.pk)
|
overrides.custom_field_ids.remove(field.pk)
|
||||||
|
|
||||||
|
def notification_action():
|
||||||
|
subject = parse_doc_title_w_placeholders(
|
||||||
|
action.notification_subject,
|
||||||
|
document.correspondent.name if document.correspondent else "",
|
||||||
|
document.document_type.name if document.document_type else "",
|
||||||
|
document.owner.username if document.owner else "",
|
||||||
|
timezone.localtime(document.added),
|
||||||
|
document.original_filename or "",
|
||||||
|
timezone.localtime(document.created),
|
||||||
|
)
|
||||||
|
body = action.notification_body.format(
|
||||||
|
title=subject,
|
||||||
|
document=document,
|
||||||
|
)
|
||||||
|
# TODO: if doc exists, construct URL
|
||||||
|
if action.notification_destination_emails:
|
||||||
|
if not settings.EMAIL_ENABLED:
|
||||||
|
logger.error(
|
||||||
|
"Email backend has not been configured, cannot send email notifications",
|
||||||
|
extra={"group": logging_group},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
email = EmailMessage(
|
||||||
|
subject=subject,
|
||||||
|
body=body,
|
||||||
|
to=action.notification_destination_emails.split(","),
|
||||||
|
)
|
||||||
|
if action.notification_include_document:
|
||||||
|
email.attach_file(document.source_path)
|
||||||
|
email.send()
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception(
|
||||||
|
f"Error occurred sending notification email: {e}",
|
||||||
|
extra={"group": logging_group},
|
||||||
|
)
|
||||||
|
if action.notification_destination_url:
|
||||||
|
try:
|
||||||
|
data = {
|
||||||
|
"title": subject,
|
||||||
|
"message": body,
|
||||||
|
}
|
||||||
|
files = None
|
||||||
|
if action.notification_include_document:
|
||||||
|
with open(document.source_path, "rb") as f:
|
||||||
|
files = {"document": f}
|
||||||
|
httpx.post(
|
||||||
|
action.notification_destination_url,
|
||||||
|
data=data,
|
||||||
|
files=files,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
httpx.post(
|
||||||
|
action.notification_destination_url,
|
||||||
|
data=data,
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception(
|
||||||
|
f"Error occurred sending notification to destination URL: {e}",
|
||||||
|
extra={"group": logging_group},
|
||||||
|
)
|
||||||
|
|
||||||
use_overrides = overrides is not None
|
use_overrides = overrides is not None
|
||||||
messages = []
|
messages = []
|
||||||
|
|
||||||
@ -911,6 +975,8 @@ def run_workflows(
|
|||||||
assignment_action()
|
assignment_action()
|
||||||
elif action.type == WorkflowAction.WorkflowActionType.REMOVAL:
|
elif action.type == WorkflowAction.WorkflowActionType.REMOVAL:
|
||||||
removal_action()
|
removal_action()
|
||||||
|
elif action.type == WorkflowAction.WorkflowActionType.NOTIFICATION:
|
||||||
|
notification_action()
|
||||||
|
|
||||||
if not use_overrides:
|
if not use_overrides:
|
||||||
# save first before setting tags
|
# save first before setting tags
|
||||||
|
@ -1195,6 +1195,7 @@ DEFAULT_FROM_EMAIL: Final[str] = os.getenv("PAPERLESS_EMAIL_FROM", EMAIL_HOST_US
|
|||||||
EMAIL_USE_TLS: Final[bool] = __get_boolean("PAPERLESS_EMAIL_USE_TLS")
|
EMAIL_USE_TLS: Final[bool] = __get_boolean("PAPERLESS_EMAIL_USE_TLS")
|
||||||
EMAIL_USE_SSL: Final[bool] = __get_boolean("PAPERLESS_EMAIL_USE_SSL")
|
EMAIL_USE_SSL: Final[bool] = __get_boolean("PAPERLESS_EMAIL_USE_SSL")
|
||||||
EMAIL_SUBJECT_PREFIX: Final[str] = "[Paperless-ngx] "
|
EMAIL_SUBJECT_PREFIX: Final[str] = "[Paperless-ngx] "
|
||||||
|
EMAIL_ENABLED = EMAIL_HOST != "localhost" or EMAIL_HOST_USER != ""
|
||||||
if DEBUG: # pragma: no cover
|
if DEBUG: # pragma: no cover
|
||||||
EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
|
EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
|
||||||
EMAIL_FILE_PATH = BASE_DIR / "sent_emails"
|
EMAIL_FILE_PATH = BASE_DIR / "sent_emails"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user