From b8e9de9e455d37e718a285e8d581a0339ff8489e Mon Sep 17 00:00:00 2001 From: Heiko Carrasco Date: Thu, 31 Oct 2019 21:46:14 +0100 Subject: [PATCH 1/4] First implementation of group mails --- notification/functions.py | 10 ++++++++-- user_group/admin.py | 21 +++++++++++++++++++++ user_group/forms.py | 4 +++- user_group/tests/test_views.py | 1 + user_group/views.py | 2 ++ 5 files changed, 35 insertions(+), 3 deletions(-) diff --git a/notification/functions.py b/notification/functions.py index 35f63db..ec7b110 100644 --- a/notification/functions.py +++ b/notification/functions.py @@ -14,7 +14,7 @@ def link_builder(ticket): def send_ticket_deadline_notification(ticket): subject = '"{0}" hat heute seine Deadline erreicht'.format(ticket.name) message = '"{0}" hat heute seine Deadline erreicht'.format(ticket.name) + "\n\n" + link_builder(ticket) - send_email(ticket.assigned_user, subject, message) + send_email(ticket, subject, message) # user: the user that edits # ticket: the ticket that has been edited @@ -50,7 +50,8 @@ def send_assigned_notification(user, ticket): send_email(ticket.assigned_user, subject, message) # sends an email, eather directly or save it as an EmailBucket to send later -def send_email(user, subject, message, frm=settings.DEFAULT_FROM_EMAIL, pdf=None): +def send_email(ticket, subject, message, frm=settings.DEFAULT_FROM_EMAIL, pdf=None): + user = ticket.assigned_user if user.sending_email_once_a_day: email = EmailBucket() email.send_to = user @@ -60,6 +61,11 @@ def send_email(user, subject, message, frm=settings.DEFAULT_FROM_EMAIL, pdf=None else: send_email_now([user.email], subject, message, frm=settings.DEFAULT_FROM_EMAIL, pdf=None) + # send mail to group + groupemail = ticket.assigned_group.groupemail.email + if groupemail != None: + send_email_now([groupemail], subject, message, frm=settings.DEFAULT_FROM_EMAIL, pdf=None) + # send the email now def send_email_now(to, subject, message, frm=settings.DEFAULT_FROM_EMAIL, pdf=None): subject = settings.EMAIL_TAG+" "+subject diff --git a/user_group/admin.py b/user_group/admin.py index 76fb6f9..ede7dac 100644 --- a/user_group/admin.py +++ b/user_group/admin.py @@ -1,6 +1,27 @@ from ajax_select.admin import AjaxSelectAdmin +from django.contrib import admin +from django.contrib.auth.models import Group + +from user_group.models import GroupEmail + + +class GroupEmailInline(admin.StackedInline): + model = GroupEmail + can_delete = False + +class GroupAdmin(admin.ModelAdmin): + inlines = (GroupEmailInline,) + list_display = ('name', 'get_group_mail') + + def get_group_mail(self, obj): + return obj.groupemail.email + + get_group_mail.short_description = 'Gruppenemailaddresse' class MyGroupAdmin(AjaxSelectAdmin): """Adds group settings to the admin page. """ pass + +admin.site.unregister(Group) +admin.site.register(Group, GroupAdmin) diff --git a/user_group/forms.py b/user_group/forms.py index 0bd90ce..42ffd59 100644 --- a/user_group/forms.py +++ b/user_group/forms.py @@ -1,4 +1,4 @@ -from django.forms import ModelForm +from django.forms import ModelForm, EmailField from ajax_select.fields import AutoCompleteSelectMultipleField from django.contrib.auth.models import Group from django.utils.translation import ugettext_lazy as _ @@ -12,6 +12,7 @@ class AddGroupForm(ModelForm): class Meta: model = Group exclude = ['permissions'] + fields = ['name', 'members'] # not part of model Group, adds a FormField for selecting members of the group members = AutoCompleteSelectMultipleField('look_members', required=True, help_text=_( @@ -20,3 +21,4 @@ class Meta: widget_options={'attrs': { 'placeholder': _( 'Suche nach Nutzern')}}) + email = EmailField(required=False, help_text=_('E-Mail Adresse der Gruppe')) diff --git a/user_group/tests/test_views.py b/user_group/tests/test_views.py index 0e8a37a..19b28c3 100644 --- a/user_group/tests/test_views.py +++ b/user_group/tests/test_views.py @@ -28,6 +28,7 @@ def test_new_group_view(self): data['name'] = 'Test group 143' data['members'] = 1 data['admins'] = 1 + data['email'] = 'hello@world.de' resp = self.client.post(url, data, follow=True) self.assertEqual(resp.status_code, 200) self.assertIn('Test group 143', str(resp.content)) diff --git a/user_group/views.py b/user_group/views.py index 3dd9435..703bfe0 100644 --- a/user_group/views.py +++ b/user_group/views.py @@ -65,7 +65,9 @@ def post(self, *args, **kwargs): form.save() # set the given users(passed as parameter members) as members of the group group.user_set.set(form.cleaned_data["members"]) + group.groupemail.email = form.cleaned_data["email"] group.save() + group.groupemail.save() messages.success(self.request, _('Gruppe {0} wurde erfolgreich editiert!'.format(get_group_name(group)))) return HttpResponseRedirect(reverse("dashboard")) From fa836938297e5d6e5ab1ec1f4664e3aa6a4c1196 Mon Sep 17 00:00:00 2001 From: Heiko Carrasco Date: Fri, 13 Dec 2019 14:50:42 +0100 Subject: [PATCH 2/4] Fix fixtures for sqlite tests, fixes #1 --- account/tests/test_forms.py | 2 +- account/tests/test_models.py | 4 ++-- account/tests/test_views.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/account/tests/test_forms.py b/account/tests/test_forms.py index 27cf719..51314bf 100644 --- a/account/tests/test_forms.py +++ b/account/tests/test_forms.py @@ -6,7 +6,7 @@ class TicketFormTest(TestCase): #install fixtures - fixtures = ['fixtures/accounts.json'] + fixtures = ['fixtures/auth_groups.json', 'fixtures/accounts.json'] #check valid login form def test_valid_login_form(self): diff --git a/account/tests/test_models.py b/account/tests/test_models.py index 077911c..e4bc3ed 100644 --- a/account/tests/test_models.py +++ b/account/tests/test_models.py @@ -3,7 +3,7 @@ #account models test cases class MyUserTest(TestCase): - fixtures = ['fixtures/accounts.json'] + fixtures = ['fixtures/auth_groups.json', 'fixtures/accounts.json'] #create test user def create_my_user(self, username='test_user', email='mail@test.com', password='Testing123'): @@ -24,4 +24,4 @@ def test_my_user_edition(self): def test_my_user_delete(self): self.assertTrue(MyUser.objects.filter(username='test_user2').exists()) user = MyUser.objects.get(username='test_user2').delete() - self.assertFalse(MyUser.objects.filter(username='test_user2').exists()) \ No newline at end of file + self.assertFalse(MyUser.objects.filter(username='test_user2').exists()) diff --git a/account/tests/test_views.py b/account/tests/test_views.py index 27ac8d5..4c33cb6 100644 --- a/account/tests/test_views.py +++ b/account/tests/test_views.py @@ -4,7 +4,7 @@ #account views test cases class MyUserViewsTest(TestCase): - fixtures = ['fixtures/accounts.json'] + fixtures = ['fixtures/auth_groups.json', 'fixtures/accounts.json'] #login view test def test_login_view(self, username='test_user', email='mail@test.com', password='Testing123'): From 057c36ede86f7264e0b488be39927574c22e5a99 Mon Sep 17 00:00:00 2001 From: Heiko Carrasco Date: Fri, 13 Dec 2019 14:51:33 +0100 Subject: [PATCH 3/4] Finialize groupemail feature --- notification/functions.py | 94 +++++++++++++++------------ pyticket/management/commands/cron.py | 1 + ticket/tests/test_mails.py | 52 +++++++++++++++ ticket/views.py | 5 +- user_group/migrations/0001_initial.py | 24 +++++++ user_group/models.py | 6 ++ 6 files changed, 138 insertions(+), 44 deletions(-) create mode 100644 ticket/tests/test_mails.py create mode 100644 user_group/migrations/0001_initial.py create mode 100644 user_group/models.py diff --git a/notification/functions.py b/notification/functions.py index ec7b110..8e142ab 100644 --- a/notification/functions.py +++ b/notification/functions.py @@ -7,73 +7,81 @@ # builds a complete link to the ticket def link_builder(ticket): - return settings.BASE_URL + reverse('show_ticket', kwargs={'ticket_id': ticket.id}) + return settings.BASE_URL + reverse('show_ticket', kwargs={'ticket_id': ticket.id}) -# user: the user that edits +# user: the user that edits # ticket: the ticket that has been edited def send_ticket_deadline_notification(ticket): - subject = '"{0}" hat heute seine Deadline erreicht'.format(ticket.name) - message = '"{0}" hat heute seine Deadline erreicht'.format(ticket.name) + "\n\n" + link_builder(ticket) - send_email(ticket, subject, message) + subject = '"{0}" hat heute seine Deadline erreicht'.format(ticket.name) + message = '"{0}" hat heute seine Deadline erreicht'.format(ticket.name) + "\n\n" + link_builder(ticket) + send_email(ticket.assigned_user, subject, message, ticket=ticket) -# user: the user that edits +# user: the user that edits # ticket: the ticket that has been edited def send_ticket_rejected_notification(user, ticket): - subject = '"{0}" wurde abgelehnt'.format(ticket.name) - message = '"{0}" wurde von {1} abgelehnt'.format(user.username, ticket.name) + "\n\n" + link_builder(ticket) + subject = '"{0}" wurde abgelehnt'.format(ticket.name) + message = '"{0}" wurde von {1} abgelehnt'.format(user.username, ticket.name) + "\n\n" + link_builder(ticket) - send_email(ticket.dispatcher, subject, message) + send_email(ticket.dispatcher, subject, message, ticket=ticket) # user: the user that commented # notify_user: the user to be notified # ticket: the commented ticket def send_user_commented_ticket(user, notify_user, ticket): - subject = '"{0}" wurde kommentiert'.format(ticket.name) - message = '"{0}" wurde von {1} kommentiert'.format(user.username, ticket.name) + "\n\n" + link_builder(ticket) + subject = '"{0}" wurde kommentiert'.format(ticket.name) + message = '"{0}" wurde von {1} kommentiert'.format(user.username, ticket.name) + "\n\n" + link_builder(ticket) - send_email(notify_user, subject, message) + send_email(notify_user, subject, message, ticket=ticket) -# user: the user that edits +# user: the user that edits # ticket: the ticket that is edited def send_ticket_edit_notification(user, ticket): - subject = '"{0}" wurde bearbeitet'.format(ticket.name) - message = '"{0}" wurde von {1} bearbeitet'.format(user.username, ticket.name) + "\n\n" + link_builder(ticket) + subject = '"{0}" wurde bearbeitet'.format(ticket.name) + message = '"{0}" wurde von {1} bearbeitet'.format(user.username, ticket.name) + "\n\n" + link_builder(ticket) - send_email(ticket.assigned_user, subject, message) + send_email(ticket.assigned_user, subject, message, ticket=ticket) -# user: the user that assigns +# user: the user that assigns # ticket: the ticket that is assigned def send_assigned_notification(user, ticket): - subject = "Dir wurde ein Ticket zugewiesen" - message = 'Dir wurde das Ticket "{0}" von {1} zugewiesen'.format(ticket.name, user.username) + "\n\n" + link_builder(ticket) + subject = "Dir wurde ein Ticket zugewiesen" + message = 'Dir wurde das Ticket "{0}" von {1} zugewiesen'.format(ticket.name, user.username) + "\n\n" + link_builder(ticket) - send_email(ticket.assigned_user, subject, message) + send_email(ticket.assigned_user, subject, message, ticket=ticket) + +def send_new_group_ticket(user, ticket): + subject = "Neues Ticket {0} in der Gruppe {1}".format(ticket.name, ticket.assigned_group.name) + message = 'Ein neues Ticket "{0}" wurde von {1} in der Gruppe {2} angelegt'.format(ticket.name, user.username, ticket.assigned_group.name) + '\n\n' + link_builder(ticket) + + send_email(ticket.assigned_user, subject, message, ticket=ticket) + # sends an email, eather directly or save it as an EmailBucket to send later -def send_email(ticket, subject, message, frm=settings.DEFAULT_FROM_EMAIL, pdf=None): - user = ticket.assigned_user - if user.sending_email_once_a_day: - email = EmailBucket() - email.send_to = user - email.subject = subject - email.message = message - email.save() - else: - send_email_now([user.email], subject, message, frm=settings.DEFAULT_FROM_EMAIL, pdf=None) +def send_email(user, subject, message, frm=settings.DEFAULT_FROM_EMAIL, pdf=None, ticket=None): + if user: + if user.sending_email_once_a_day: + email = EmailBucket() + email.send_to = user + email.subject = subject + email.message = message + email.save() + else: + send_email_now([user.email], subject, message, frm=settings.DEFAULT_FROM_EMAIL, pdf=None) - # send mail to group - groupemail = ticket.assigned_group.groupemail.email - if groupemail != None: - send_email_now([groupemail], subject, message, frm=settings.DEFAULT_FROM_EMAIL, pdf=None) + if ticket and ticket.assigned_group and ticket.assigned_group.groupemail: + # send mail to group + groupemail = ticket.assigned_group.groupemail.email + if groupemail != None: + send_email_now([groupemail], subject, message, frm=settings.DEFAULT_FROM_EMAIL, pdf=None) # send the email now def send_email_now(to, subject, message, frm=settings.DEFAULT_FROM_EMAIL, pdf=None): - subject = settings.EMAIL_TAG+" "+subject - rendered_subject = render_to_string("notification/email/subject.txt", { "message": message, "subject": subject }) - rendered_message = render_to_string("notification/email/message.txt", { "message": message, "subject": subject }) - # html_message = render_to_string("notification/email/email_base.html", { "message": message, "subject": subject, "settings": settings }) - msg = EmailMultiAlternatives(subject, rendered_message, frm, to) - # msg.attach_alternative(html_message, "text/html") - if pdf: - msg.attach_file(pdf) - msg.send() + subject = settings.EMAIL_TAG+" "+subject + rendered_subject = render_to_string("notification/email/subject.txt", { "message": message, "subject": subject }) + rendered_message = render_to_string("notification/email/message.txt", { "message": message, "subject": subject }) + # html_message = render_to_string("notification/email/email_base.html", { "message": message, "subject": subject, "settings": settings }) + msg = EmailMultiAlternatives(subject, rendered_message, frm, to) + # msg.attach_alternative(html_message, "text/html") + if pdf: + msg.attach_file(pdf) + msg.send() diff --git a/pyticket/management/commands/cron.py b/pyticket/management/commands/cron.py index 7827ead..bf16189 100644 --- a/pyticket/management/commands/cron.py +++ b/pyticket/management/commands/cron.py @@ -3,6 +3,7 @@ import pytz from django.core.management.base import BaseCommand, CommandError +from django.contrib.auth.models import Group from ticket.models import Ticket from account.models import MyUser from notification.models import EmailBucket diff --git a/ticket/tests/test_mails.py b/ticket/tests/test_mails.py new file mode 100644 index 0000000..6a0c2e4 --- /dev/null +++ b/ticket/tests/test_mails.py @@ -0,0 +1,52 @@ +from django.urls import reverse +from django.test.client import Client +from django.test import TestCase +from django.core import mail +from django.contrib.auth.models import Group +from django.contrib.auth import get_user_model + +from user_group.models import GroupEmail +from ticket.models import Ticket +from notification.functions import link_builder + + +class MailTest(TestCase): + + def setUp(self): + self.group = Group.objects.create(name="mailtestgroup") + self.group.save() + group_mail = GroupEmail.objects.create(group=self.group, email="test@test.de") + group_mail.save() + u = get_user_model().objects.create_user(username="mailtestuser") + u.groups.add(self.group) + u.save() + + self.client = Client() + self.client.force_login(u) + + def test_group_mail(self): + url = reverse("new_ticket") + resp = self.client.get(url) + + form = resp.context["form"] + data = form.initial + data["name"] = "Mail Ticket" + data["text"] = "..." + data["assigned_user"] = "" + data["priority"] = "normal" + data["assigned_group"] = self.group.id + resp = self.client.post(url, data, follow=True) + self.assertEqual(resp.status_code, 200) + self.assertTrue(Ticket.objects.filter(name="Mail Ticket").exists()) + + self.assertEqual(len(mail.outbox), 1) + self.assertEqual( + mail.outbox[0].subject, + "[Ticketsystem] Neues Ticket Mail Ticket in der Gruppe mailtestgroup", + ) + self.assertEqual( + mail.outbox[0].body, + " Ein neues Ticket "Mail Ticket" wurde von mailtestuser in der Gruppe mailtestgroup angelegt\n\n{} ".format( + link_builder(Ticket.objects.get(name="Mail Ticket")) + ), + ) diff --git a/ticket/views.py b/ticket/views.py index d5ef519..1864975 100644 --- a/ticket/views.py +++ b/ticket/views.py @@ -16,7 +16,7 @@ from .forms import AddTicketForm, AddCommentForm, EditTicketForm from notification.functions import send_email, send_assigned_notification, \ send_ticket_edit_notification, send_user_commented_ticket, \ - send_ticket_rejected_notification, send_ticket_deadline_notification, send_email_now + send_ticket_rejected_notification, send_ticket_deadline_notification, send_email_now, send_new_group_ticket import copy from django.utils.text import Truncator import bleach @@ -59,6 +59,9 @@ def post(self, *args, **kwargs): # save the changes made instance.save() + if instance.assigned_group: + send_new_group_ticket(user, instance) + ticket = form.instance messages.success(self.request, diff --git a/user_group/migrations/0001_initial.py b/user_group/migrations/0001_initial.py new file mode 100644 index 0000000..7a93ed9 --- /dev/null +++ b/user_group/migrations/0001_initial.py @@ -0,0 +1,24 @@ +# Generated by Django 2.2.4 on 2019-09-12 17:57 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0011_update_proxy_permissions'), + ] + + operations = [ + migrations.CreateModel( + name='GroupEmail', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('email', models.EmailField(blank=True, max_length=254)), + ('group', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='auth.Group')), + ], + ), + ] diff --git a/user_group/models.py b/user_group/models.py new file mode 100644 index 0000000..7a72a18 --- /dev/null +++ b/user_group/models.py @@ -0,0 +1,6 @@ +from django.db import models +from django.contrib.auth.models import Group + +class GroupEmail(models.Model): + group = models.OneToOneField(Group, on_delete=models.CASCADE) + email = models.EmailField(blank=True) From 3d186ff7ecf7b0b30485d0fe1e9f2a72f3223769 Mon Sep 17 00:00:00 2001 From: Heiko Carrasco Date: Fri, 13 Dec 2019 14:52:07 +0100 Subject: [PATCH 4/4] Remove python 3.5 from tests and add 3.8 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a113d29..078d635 100755 --- a/.travis.yml +++ b/.travis.yml @@ -20,9 +20,9 @@ addons: firefox: latest python: - - "3.5" - "3.6" - "3.7" + - "3.8" before_install: - wget https://github.com/mozilla/geckodriver/releases/download/v0.24.0/geckodriver-v0.24.0-linux64.tar.gz