mirror of
https://github.com/inventree/InvenTree.git
synced 2025-09-13 14:11:37 +00:00
Email history enhancement (#10114)
* add warning that the log is useless by default * Add setting to enhance email log again * add missing test for #10109 * add test for delete protections * add error code * Update email.md * Update email.md
This commit is contained in:
@@ -10,7 +10,7 @@ from typing import Callable, Optional
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core.exceptions import AppRegistryNotReady
|
||||
from django.core.exceptions import AppRegistryNotReady, ValidationError
|
||||
from django.core.management import call_command
|
||||
from django.db import DEFAULT_DB_ALIAS, connections
|
||||
from django.db.migrations.executor import MigrationExecutor
|
||||
@@ -479,10 +479,16 @@ def delete_old_emails():
|
||||
emails = EmailMessage.objects.filter(timestamp__lte=threshold)
|
||||
|
||||
if emails.count() > 0:
|
||||
logger.info('Deleted %s old email messages', emails.count())
|
||||
emails.delete()
|
||||
try:
|
||||
emails.delete()
|
||||
logger.info('Deleted %s old email messages', emails.count())
|
||||
except ValidationError:
|
||||
logger.info(
|
||||
'Did not delete %s old email messages because of a validation error',
|
||||
emails.count(),
|
||||
)
|
||||
|
||||
except AppRegistryNotReady:
|
||||
except AppRegistryNotReady: # pragma: no cover
|
||||
logger.info("Could not perform 'delete_old_emails' - App registry not ready")
|
||||
|
||||
|
||||
|
@@ -239,3 +239,61 @@ class InvenTreeTaskTests(PluginRegistryMixin, TestCase):
|
||||
msg.message,
|
||||
"Background worker task 'InvenTree.tasks.failed_task' failed after 10 attempts",
|
||||
)
|
||||
|
||||
def test_delete_old_emails(self):
|
||||
"""Test the delete_old_emails task."""
|
||||
from common.models import EmailMessage
|
||||
|
||||
# Create an email message
|
||||
self.create_mails()
|
||||
|
||||
# Run the task
|
||||
InvenTreeSetting.set_setting('INVENTREE_DELETE_EMAIL_DAYS', 31)
|
||||
InvenTree.tasks.offload_task(InvenTree.tasks.delete_old_emails, force_sync=True)
|
||||
|
||||
# Check that the email message has been deleted
|
||||
emails = EmailMessage.objects.all()
|
||||
self.assertEqual(len(emails), 1)
|
||||
self.assertEqual(emails[0].subject, 'Test Email 2')
|
||||
|
||||
# Set the setting higher than the threshold
|
||||
InvenTreeSetting.set_setting('INVENTREE_DELETE_EMAIL_DAYS', 30)
|
||||
|
||||
# Run the task again
|
||||
InvenTree.tasks.offload_task(InvenTree.tasks.delete_old_emails, force_sync=True)
|
||||
emails = EmailMessage.objects.all()
|
||||
self.assertEqual(len(emails), 0)
|
||||
|
||||
# Re-Add messages and enable a proper log
|
||||
self.create_mails()
|
||||
|
||||
# Set the setting lower than the threshold
|
||||
InvenTreeSetting.set_setting('INVENTREE_DELETE_EMAIL_DAYS', 7)
|
||||
InvenTreeSetting.set_setting('INVENTREE_PROTECT_EMAIL_LOG', True)
|
||||
|
||||
# Run the task again
|
||||
InvenTree.tasks.offload_task(InvenTree.tasks.delete_old_emails, force_sync=True)
|
||||
|
||||
# Check that the email message has not been deleted
|
||||
emails = EmailMessage.objects.all()
|
||||
self.assertEqual(len(emails), 2)
|
||||
|
||||
def create_mails(self):
|
||||
"""Create some email messages for testing."""
|
||||
from common.models import EmailMessage
|
||||
|
||||
start_mails = [
|
||||
['Test Email 1', 'This is a test email.', 'abc@example.org', threshold_low],
|
||||
[
|
||||
'Test Email 2',
|
||||
'This is another test email.',
|
||||
'def@example.org',
|
||||
threshold,
|
||||
],
|
||||
]
|
||||
for subject, body, to, timestamp in start_mails:
|
||||
msg = EmailMessage.objects.create(
|
||||
subject=subject, body=body, to=to, priority=1
|
||||
)
|
||||
msg.timestamp = timestamp
|
||||
msg.save()
|
||||
|
@@ -54,7 +54,7 @@ import InvenTree.ready
|
||||
import InvenTree.tasks
|
||||
import users.models
|
||||
from common.setting.type import InvenTreeSettingsKeyType, SettingsKeyType
|
||||
from common.settings import global_setting_overrides
|
||||
from common.settings import get_global_setting, global_setting_overrides
|
||||
from generic.enums import StringEnum
|
||||
from generic.states import ColorEnum
|
||||
from generic.states.custom import state_color_mappings
|
||||
@@ -2522,6 +2522,28 @@ class Priority(models.IntegerChoices):
|
||||
HEADER_PRIORITY = 'X-Priority'
|
||||
HEADER_MSG_ID = 'Message-ID'
|
||||
|
||||
del_error_msg = _(
|
||||
'INVE-E8: Email log deletion is protected. Set INVENTREE_PROTECT_EMAIL_LOG to False to allow deletion.'
|
||||
)
|
||||
|
||||
|
||||
class NoDeleteQuerySet(models.query.QuerySet):
|
||||
"""Custom QuerySet to prevent deletion of EmailLog entries."""
|
||||
|
||||
def delete(self):
|
||||
"""Override delete method to prevent deletion of EmailLog entries."""
|
||||
if get_global_setting('INVENTREE_PROTECT_EMAIL_LOG'):
|
||||
raise ValidationError(del_error_msg)
|
||||
super().delete()
|
||||
|
||||
|
||||
class NoDeleteManager(models.Manager):
|
||||
"""Custom Manager to use NoDeleteQuerySet."""
|
||||
|
||||
def get_queryset(self):
|
||||
"""Return a NoDeleteQuerySet."""
|
||||
return NoDeleteQuerySet(self.model, using=self._db)
|
||||
|
||||
|
||||
class EmailMessage(models.Model):
|
||||
"""Model for storing email messages sent or received by the system.
|
||||
@@ -2663,6 +2685,14 @@ class EmailMessage(models.Model):
|
||||
|
||||
return ret
|
||||
|
||||
objects = NoDeleteManager()
|
||||
|
||||
def delete(self, *kwargs):
|
||||
"""Delete entry - if not protected."""
|
||||
if get_global_setting('INVENTREE_PROTECT_EMAIL_LOG'):
|
||||
raise ValidationError(del_error_msg)
|
||||
return super().delete(*kwargs)
|
||||
|
||||
|
||||
class EmailThread(InvenTree.models.InvenTreeMetadataModel):
|
||||
"""Model for storing email threads."""
|
||||
|
@@ -340,6 +340,12 @@ SYSTEM_SETTINGS: dict[str, InvenTreeSettingsKeyType] = {
|
||||
'units': _('days'),
|
||||
'validator': [int, MinValueValidator(7)],
|
||||
},
|
||||
'INVENTREE_PROTECT_EMAIL_LOG': {
|
||||
'name': _('Protect Email Log'),
|
||||
'description': _('Prevent deletion of email log entries'),
|
||||
'default': False,
|
||||
'validator': bool,
|
||||
},
|
||||
'BARCODE_ENABLE': {
|
||||
'name': _('Barcode Support'),
|
||||
'description': _('Enable barcode scanner support in the web interface'),
|
||||
|
@@ -10,6 +10,7 @@ from anymail.inbound import AnymailInboundMessage
|
||||
from anymail.signals import AnymailInboundEvent, AnymailTrackingEvent, inbound, tracking
|
||||
|
||||
from common.models import EmailMessage, Priority
|
||||
from common.settings import set_global_setting
|
||||
from InvenTree.helpers_email import send_email
|
||||
from InvenTree.unit_test import InvenTreeAPITestCase
|
||||
|
||||
@@ -129,6 +130,31 @@ class EmailTests(InvenTreeAPITestCase):
|
||||
self.assertEqual(msg.status, EmailMessage.EmailStatus.FAILED)
|
||||
self.assertEqual(msg.error_message, 'Test error sending email')
|
||||
|
||||
def test_email_model_delete(self):
|
||||
"""Test that the email model does not allow deletion if disabled."""
|
||||
set_global_setting('INVENTREE_PROTECT_EMAIL_LOG', True)
|
||||
EmailMessage.objects.create(
|
||||
subject='test sub', body='test msg', to='abc@example.org', priority=3
|
||||
)
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
EmailMessage.objects.all().delete()
|
||||
|
||||
msg = EmailMessage.objects.create(
|
||||
subject='test sub', body='test msg', to='abc@example.org', priority=3
|
||||
)
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
msg.delete()
|
||||
|
||||
# Should still work without the protection
|
||||
self.assertEqual(EmailMessage.objects.count(), 2)
|
||||
set_global_setting('INVENTREE_PROTECT_EMAIL_LOG', False)
|
||||
msg.delete()
|
||||
|
||||
# Check that the message was deleted
|
||||
self.assertEqual(EmailMessage.objects.count(), 1)
|
||||
|
||||
|
||||
class EmailEventsTests(TestCase):
|
||||
"""Unit tests for anymail events."""
|
||||
|
@@ -66,7 +66,8 @@ export default function SystemSettings() {
|
||||
'INVENTREE_DELETE_TASKS_DAYS',
|
||||
'INVENTREE_DELETE_ERRORS_DAYS',
|
||||
'INVENTREE_DELETE_NOTIFICATIONS_DAYS',
|
||||
'INVENTREE_DELETE_EMAIL_DAYS'
|
||||
'INVENTREE_DELETE_EMAIL_DAYS',
|
||||
'INVENTREE_PROTECT_EMAIL_LOG'
|
||||
]}
|
||||
/>
|
||||
)
|
||||
|
Reference in New Issue
Block a user