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 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'): diff --git a/notification/functions.py b/notification/functions.py index 35f63db..8e142ab 100644 --- a/notification/functions.py +++ b/notification/functions.py @@ -7,67 +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.assigned_user, 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(user, subject, message, frm=settings.DEFAULT_FROM_EMAIL, pdf=None): - 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) + + 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/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/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) 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"))