diff --git a/tickets/admin.py b/tickets/admin.py index 69f84d0d..1644ee75 100644 --- a/tickets/admin.py +++ b/tickets/admin.py @@ -25,13 +25,15 @@ Ticket preferences model from django.contrib import admin -from .models import Ticket +from .models import Ticket, CommentTicket from reversion.admin import VersionAdmin class TicketAdmin(VersionAdmin): - """Gestion des ticket""" + pass +class CommentTicketAdmin(VersionAdmin): pass admin.site.register(Ticket, TicketAdmin) +admin.site.register(CommentTicket, CommentTicketAdmin) diff --git a/tickets/forms.py b/tickets/forms.py index b1be6597..a9f6617d 100644 --- a/tickets/forms.py +++ b/tickets/forms.py @@ -31,7 +31,7 @@ from re2o.field_permissions import FieldPermissionFormMixin from re2o.mixins import FormRevMixin from django.utils.translation import ugettext_lazy as _ -from .models import Ticket +from .models import Ticket, CommentTicket class NewTicketForm(FormRevMixin, ModelForm): @@ -48,7 +48,7 @@ class NewTicketForm(FormRevMixin, ModelForm): self.fields.pop('email') self.instance.user = request.user self.fields['description'].help_text = render_to_string('tickets/help_text.html') - self.instance.request = request + self.instance.language = getattr(request, "LANGUAGE_CODE", "en") class EditTicketForm(FormRevMixin, ModelForm): @@ -62,3 +62,16 @@ class EditTicketForm(FormRevMixin, ModelForm): super(EditTicketForm, self).__init__(*args, **kwargs) self.fields['email'].required = False + +class CommentTicketForm(FormRevMixin, ModelForm): + """Edit and create comment to a ticket""" + + class Meta: + model = CommentTicket + fields = ["comment"] + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop("prefix", self.Meta.model.__name__) + super(CommentTicketForm, self).__init__(*args, prefix=prefix, **kwargs) + self.fields["comment"].label = _("comment") + diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index 05ce5d9b..415c618f 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 22:06+0200\n" +"POT-Creation-Date: 2020-04-23 03:10+0200\n" "PO-Revision-Date: 2019-11-16 00:35+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -30,44 +30,68 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: tickets/models.py:54 +#: tickets/forms.py:76 +msgid "comment" +msgstr "commentaire" + +#: tickets/models.py:57 msgid "Title of the ticket." msgstr "Titre du ticket." -#: tickets/models.py:63 +#: tickets/models.py:66 msgid "An email address to get back to you." msgstr "Une adresse mail pour vous recontacter." -#: tickets/models.py:69 +#: tickets/models.py:70 +msgid "Language of the ticket." +msgstr "Langue des tickets" + +#: tickets/models.py:74 tickets/models.py:170 msgid "Can view a ticket object" msgstr "Peut voir un objet ticket" -#: tickets/models.py:70 +#: tickets/models.py:75 tickets/models.py:171 msgid "ticket" msgstr "ticket" -#: tickets/models.py:71 +#: tickets/models.py:76 tickets/models.py:172 msgid "tickets" msgstr "tickets" -#: tickets/models.py:75 +#: tickets/models.py:80 #, python-format msgid "Ticket from %(name)s. Date: %(date)s." msgstr "Ticket de %(name)s. Date : %(date)s." -#: tickets/models.py:77 +#: tickets/models.py:82 #, python-format msgid "Anonymous ticket. Date: %s." msgstr "Ticket anonyme. Date : %s." -#: tickets/models.py:111 +#: tickets/models.py:90 tickets/templates/tickets/aff_ticket.html:52 +msgid "Anonymous user" +msgstr "Utilisateur anonyme" + +#: tickets/models.py:128 msgid "You don't have the right to view other tickets than yours." msgstr "Vous n'avez pas le droit de voir d'autres tickets que les vôtres." -#: tickets/models.py:123 +#: tickets/models.py:140 tickets/models.py:214 msgid "You don't have the right to view the list of tickets." msgstr "Vous n'avez pas le droit de voir la liste des tickets." +#: tickets/models.py:187 +msgid "You don't have the right to view other tickets comments than yours." +msgstr "Vous n'avez pas le droit de voir d'autres tickets que les vôtres." + +#: tickets/models.py:202 +msgid "You don't have the right to edit other tickets comments than yours." +msgstr "Vous n'avez pas le droit d'éditer d'autres tickets que les vôtres." + +#: tickets/models.py:232 +msgid "Update of your ticket" +msgstr "Mise à jour de votre ticket" + #: tickets/preferences/forms.py:44 msgid "Publish address" msgstr "Adresse mail de publication" @@ -87,7 +111,7 @@ msgstr "Options des tickets" msgid "Can view tickets options" msgstr "Peut voir les options des tickets" -#: tickets/templates/tickets/aff_ticket.html:31 +#: tickets/templates/tickets/aff_ticket.html:32 #: tickets/templates/tickets/contact.html:4 #: tickets/templates/tickets/index.html:29 #: tickets/templates/tickets/preferences.html:6 @@ -95,57 +119,79 @@ msgstr "Peut voir les options des tickets" msgid "Tickets" msgstr "Tickets" -#: tickets/templates/tickets/aff_ticket.html:35 +#: tickets/templates/tickets/aff_ticket.html:36 #, python-format msgid "Ticket #%(id)s" msgstr "Ticket #%(id)s" -#: tickets/templates/tickets/aff_ticket.html:37 +#: tickets/templates/tickets/aff_ticket.html:38 #: tickets/templates/tickets/aff_tickets.html:58 msgid "Solved" msgstr "Résolu" -#: tickets/templates/tickets/aff_ticket.html:39 +#: tickets/templates/tickets/aff_ticket.html:40 msgid "Not solved" msgstr "Non résolu" -#: tickets/templates/tickets/aff_ticket.html:45 +#: tickets/templates/tickets/aff_ticket.html:46 msgid "Opened by" msgstr "Ouvert par" -#: tickets/templates/tickets/aff_ticket.html:51 -msgid "Anonymous user" -msgstr "Utilisateur anonyme" - -#: tickets/templates/tickets/aff_ticket.html:55 +#: tickets/templates/tickets/aff_ticket.html:56 msgid "Response address: " msgstr "Adresse de réponse : " -#: tickets/templates/tickets/aff_ticket.html:55 +#: tickets/templates/tickets/aff_ticket.html:56 msgid "Response to your ticket" msgstr "Réponse à votre ticket" -#: tickets/templates/tickets/aff_ticket.html:60 -msgid "Title:" -msgstr "Titre :" - #: tickets/templates/tickets/aff_ticket.html:61 -msgid "Description:" -msgstr "Description :" +msgid "Add a comment " +msgstr "Ajouter un commentaire" -#: tickets/templates/tickets/aff_ticket.html:65 -msgid "Edit this ticket" -msgstr "Modifier le ticket." +#: tickets/templates/tickets/aff_ticket.html:64 +#: tickets/templates/tickets/preferences.html:14 tickets/views.py:153 +msgid "Edit" +msgstr "Modifier" -#: tickets/templates/tickets/aff_ticket.html:67 +#: tickets/templates/tickets/aff_ticket.html:66 msgid "Mark as solved" msgstr "Marquer comme résolu" -#: tickets/templates/tickets/aff_ticket.html:69 +#: tickets/templates/tickets/aff_ticket.html:68 msgid "Mark as unsolved" msgstr "Marquer comme non résolu" -#: tickets/templates/tickets/aff_ticket.html:78 +#: tickets/templates/tickets/aff_ticket.html:76 +msgid "Title:" +msgstr "Titre :" + +#: tickets/templates/tickets/aff_ticket.html:77 +#: tickets/templates/tickets/aff_ticket.html:84 +msgid "Description:" +msgstr "Description :" + +#: tickets/templates/tickets/aff_ticket.html:83 +msgid "Comment " +msgstr "Commentaire" + +#: tickets/templates/tickets/aff_ticket.html:83 +msgid " added by " +msgstr " ajouté par " + +#: tickets/templates/tickets/aff_ticket.html:83 +msgid " on " +msgstr " le " + +#: tickets/templates/tickets/aff_ticket.html:87 +msgid "Edit this comment " +msgstr "Modifier le commentaire" + +#: tickets/templates/tickets/aff_ticket.html:90 +msgid "Delete this comment " +msgstr "Supprimer ce commentaire" + +#: tickets/templates/tickets/aff_ticket.html:99 msgid "All tickets" msgstr "Tous les tickets" @@ -200,6 +246,21 @@ msgstr "" msgid "Open a ticket" msgstr "Ouvrir un ticket" +#: tickets/templates/tickets/delete.html:29 +msgid "Deletion of tickets" +msgstr "Suppression de tickets" + +#: tickets/templates/tickets/delete.html:35 +#, python-format +msgid "" +"Warning: are you sure you want to delete this %(objet_name)s object " +"( %(objet)s )?" +msgstr "" + +#: tickets/templates/tickets/delete.html:36 +msgid "Confirm" +msgstr "Confirmer" + #: tickets/templates/tickets/edit.html:34 msgid "Ticket opening" msgstr "Ouverture de ticket" @@ -242,10 +303,6 @@ msgstr "Liste des tickets" msgid "Manage the tickets" msgstr "Gérer les tickets" -#: tickets/templates/tickets/preferences.html:14 -msgid "Edit" -msgstr "Modifier" - #: tickets/templates/tickets/preferences.html:21 msgid "Publication email address" msgstr "Adresse mail de publication" @@ -258,18 +315,38 @@ msgstr "Pas d'adresse mail, les tickets ne seront pas publiés." msgid "No tickets" msgstr "Pas de tickets" -#: tickets/views.py:56 +#: tickets/views.py:62 msgid "" "Your ticket has been succesfully opened. We will take care of it as soon as " "possible." msgstr "" "Votre ticket a bien été ouvert. Nous nous en occuperons dès que possible." -#: tickets/views.py:102 +#: tickets/views.py:109 msgid "Ticket has been updated successfully" msgstr "Le ticket a été mis à jour" -#: tickets/views.py:123 tickets/views.py:148 +#: tickets/views.py:130 +msgid "This comment was added." +msgstr "Le commentaire a été ajouté" + +#: tickets/views.py:135 +msgid "Add a comment" +msgstr "Ajouter un commentaire" + +#: tickets/views.py:148 +msgid "This comment was edited." +msgstr "Le commentaire a été édité" + +#: tickets/views.py:164 +msgid "The comment was deleted." +msgstr "Le commentaire a été supprimé" + +#: tickets/views.py:169 +msgid "Ticket Comment" +msgstr "Commentaire de ticket" + +#: tickets/views.py:183 tickets/views.py:208 msgid "Never" msgstr "Jamais" @@ -298,9 +375,6 @@ msgstr "Jamais" #~ "Vous n'êtes pas authentifié. Veuillez vous connecter ou fournir une " #~ "adresse mail pour que nous puissions vous recontacter." -#~ msgid "Open the ticket" -#~ msgstr "Ouvrir le ticket" - #~ msgid "Email language" #~ msgstr "Langue du mail" diff --git a/tickets/migrations/0005_auto_20200422_2309.py b/tickets/migrations/0005_auto_20200422_2309.py new file mode 100644 index 00000000..43fdbdb3 --- /dev/null +++ b/tickets/migrations/0005_auto_20200422_2309.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-22 21:09 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import re2o.mixins + + +class Migration(migrations.Migration): + + dependencies = [ + ('tickets', '0004_auto_20200422_2127'), + ] + + operations = [ + migrations.CreateModel( + name='CommentTicket', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateTimeField(auto_now_add=True)), + ('comment', models.TextField(max_length=4095)), + ], + options={ + 'verbose_name': 'ticket', + 'verbose_name_plural': 'tickets', + 'permissions': (('view_commentticket', 'Can view a ticket object'),), + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + migrations.AlterModelOptions( + name='ticket', + options={'permissions': (('view_ticket', 'Can view a ticket object'),), 'verbose_name': 'ticket', 'verbose_name_plural': 'tickets'}, + ), + migrations.AddField( + model_name='commentticket', + name='parent_ticket', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tickets.Ticket'), + ), + ] diff --git a/tickets/migrations/0006_auto_20200423_0202.py b/tickets/migrations/0006_auto_20200423_0202.py new file mode 100644 index 00000000..565a8b7a --- /dev/null +++ b/tickets/migrations/0006_auto_20200423_0202.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-23 00:02 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('tickets', '0005_auto_20200422_2309'), + ] + + operations = [ + migrations.AddField( + model_name='commentticket', + name='created_at', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='commentticket', + name='created_by', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ticket_comment', to=settings.AUTH_USER_MODEL), + preserve_default=False, + ), + ] diff --git a/tickets/migrations/0007_ticket_language.py b/tickets/migrations/0007_ticket_language.py new file mode 100644 index 00000000..351d2359 --- /dev/null +++ b/tickets/migrations/0007_ticket_language.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-23 01:05 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tickets', '0006_auto_20200423_0202'), + ] + + operations = [ + migrations.AddField( + model_name='ticket', + name='language', + field=models.CharField(default='en', help_text='Language of the ticket.', max_length=16), + ), + ] diff --git a/tickets/models.py b/tickets/models.py index 5cc7d003..67bf8f34 100644 --- a/tickets/models.py +++ b/tickets/models.py @@ -29,6 +29,9 @@ from django.utils.translation import ugettext_lazy as _ from django.template import loader from django.db.models.signals import post_save from django.dispatch import receiver +from django.utils.functional import cached_property + +from reversion.models import Version from re2o.mixins import AclMixin from django.core.mail import EmailMessage @@ -63,10 +66,12 @@ class Ticket(AclMixin, models.Model): help_text=_("An email address to get back to you."), max_length=100, null=True ) solved = models.BooleanField(default=False) - request = None + language = models.CharField( + max_length=16, help_text=_("Language of the ticket."), default="en" + ) class Meta: - permissions = (("view_tickets", _("Can view a ticket object")),) + permissions = (("view_ticket", _("Can view a ticket object")),) verbose_name = _("ticket") verbose_name_plural = _("tickets") @@ -76,13 +81,25 @@ class Ticket(AclMixin, models.Model): else: return _("Anonymous ticket. Date: %s.") % (self.date) + @cached_property + def opened_by(self): + """Return full name of this ticket opener""" + if self.user: + return self.user.get_full_name() + else: + return _("Anonymous user") + + @cached_property + def get_mail(self): + """Return the mail of the owner of this ticket""" + return self.email or self.user.get_mail + def publish_mail(self): site_url = GeneralOption.get_cached_value("main_site_url") to_addr = TicketOption.get_cached_value("publish_address") context = {"ticket": self, "site_url": site_url} - language = getattr(self.request, "LANGUAGE_CODE", "en") - if language == "fr": + if self.language == "fr": obj = "Nouveau ticket ouvert" template = loader.get_template("tickets/publication_mail_fr") else: @@ -94,7 +111,7 @@ class Ticket(AclMixin, models.Model): template.render(context), GeneralOption.get_cached_value("email_from"), [to_addr], - reply_to=[self.email], + reply_to=[self.get_mail], ) mail_to_send.send(fail_silently=False) @@ -103,13 +120,13 @@ class Ticket(AclMixin, models.Model): """ Check that the user has the right to view the ticket or that it is the author""" if ( - not user_request.has_perm("tickets.view_tickets") + not user_request.has_perm("tickets.view_ticket") and self.user != user_request ): return ( False, _("You don't have the right to view other tickets than yours."), - ("tickets.view_tickets",), + ("tickets.view_ticket",), ) else: return True, None, None @@ -117,13 +134,13 @@ class Ticket(AclMixin, models.Model): @staticmethod def can_view_all(user_request, *_args, **_kwargs): """ Check that the user has access to the list of all tickets""" - can = user_request.has_perm("tickets.view_tickets") + can = user_request.has_perm("tickets.view_ticket") return ( can, _("You don't have the right to view the list of tickets.") if not can else None, - ("tickets.view_tickets",), + ("tickets.view_ticket",), ) def can_create(user_request, *_args, **_kwargs): @@ -131,6 +148,97 @@ class Ticket(AclMixin, models.Model): return True, None, None +class CommentTicket(AclMixin, models.Model): + """A comment of a ticket""" + date = models.DateTimeField(auto_now_add=True) + comment = models.TextField( + max_length=4095, + blank=False, + null=False, + ) + parent_ticket = models.ForeignKey( + "Ticket", on_delete=models.CASCADE + ) + created_at = models.DateTimeField(auto_now_add=True) + created_by = models.ForeignKey( + "users.User", + on_delete=models.CASCADE, + related_name="ticket_comment", + ) + + class Meta: + permissions = (("view_commentticket", _("Can view a ticket object")),) + verbose_name = _("ticket") + verbose_name_plural = _("tickets") + + @cached_property + def comment_id(self): + return CommentTicket.objects.filter(parent_ticket=self.parent_ticket, pk__lt=self.pk).count() + 1 + + def can_view(self, user_request, *_args, **_kwargs): + """ Check that the user has the right to view the ticket comment + or that it is the author""" + if ( + not user_request.has_perm("tickets.view_commentticket") + and self.parent_ticket.user != user_request + ): + return ( + False, + _("You don't have the right to view other tickets comments than yours."), + ("tickets.view_commentticket",), + ) + else: + return True, None, None + + def can_edit(self, user_request, *_args, **_kwargs): + """ Check that the user has the right to edit the ticket comment + or that it is the author""" + if ( + not user_request.has_perm("tickets.edit_commentticket") + and (self.parent_ticket.user != user_request or self.parent_ticket.user != self.created_by) + ): + return ( + False, + _("You don't have the right to edit other tickets comments than yours."), + ("tickets.edit_commentticket",), + ) + else: + return True, None, None + + @staticmethod + def can_view_all(user_request, *_args, **_kwargs): + """ Check that the user has access to the list of all tickets comments""" + can = user_request.has_perm("tickets.view_commentticket") + return ( + can, + _("You don't have the right to view the list of tickets.") + if not can + else None, + ("tickets.view_commentticket",), + ) + + def __str__(self): + return "Comment " + str(self.comment_id) + " on " + str(self.parent_ticket) + + def publish_mail(self): + site_url = GeneralOption.get_cached_value("main_site_url") + to_addr = TicketOption.get_cached_value("publish_address") + context = {"comment": self, "site_url": site_url} + + if self.parent_ticket.language == "fr": + template = loader.get_template("tickets/update_mail_fr") + else: + template = loader.get_template("tickets/update_mail_en") + obj = _("Update of your ticket") + mail_to_send = EmailMessage( + obj, + template.render(context), + GeneralOption.get_cached_value("email_from"), + [to_addr, self.parent_ticket.get_mail], + ) + mail_to_send.send(fail_silently=False) + + @receiver(post_save, sender=Ticket) def ticket_post_save(**kwargs): """ Send the mail to publish the new ticket """ @@ -138,3 +246,12 @@ def ticket_post_save(**kwargs): if TicketOption.get_cached_value("publish_address"): ticket = kwargs["instance"] ticket.publish_mail() + + +@receiver(post_save, sender=CommentTicket) +def comment_post_save(**kwargs): + """ Send the mail to publish the new comment """ + if kwargs["created"]: + if TicketOption.get_cached_value("publish_address"): + comment = kwargs["instance"] + comment.publish_mail() diff --git a/tickets/templates/tickets/aff_ticket.html b/tickets/templates/tickets/aff_ticket.html index c4b2625d..da7d0c39 100644 --- a/tickets/templates/tickets/aff_ticket.html +++ b/tickets/templates/tickets/aff_ticket.html @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load i18n %} {% load humanize %} +{% load logs_extra %} {% load acl %} {% block title %}{% trans "Tickets" %}{% endblock %} @@ -54,28 +55,48 @@ with this program; if not, write to the Free Software Foundation, Inc., {% if not ticket.user %} {% trans "Response address: " %}{{ticket.email}} {% endif %} + +
+ {% can_view ticket %} + {% trans "Add a comment " %} + {% acl_end %} + {% can_edit ticket %} + {% trans "Edit" %} + {% if not ticket.solved %} + {% trans "Mark as solved" %} + {% else %} + {% trans "Mark as unsolved" %} + {% endif %} + {% acl_end %} + {% history_button ticket text=True %} +

{% trans "Title:" %} {{ticket.title}}

-

{% trans "Description:" %} {{ ticket.description | linebreaks }}

+ {% trans "Description:" %} {{ ticket.description | linebreaks }} -
- {% can_edit ticket %} -

{% trans "Edit this ticket" %}

- {% if not ticket.solved %} -

{% trans "Mark as solved" %}

- {% else %} -

{% trans "Mark as unsolved" %}

- {% endif %} +
+ +{% for comment in comments %} + +{% endfor %}
- -
-

{% trans "All tickets" %}

+ {% trans "All tickets" %}
{% endblock %} diff --git a/tickets/templates/tickets/delete.html b/tickets/templates/tickets/delete.html new file mode 100644 index 00000000..c583ee5b --- /dev/null +++ b/tickets/templates/tickets/delete.html @@ -0,0 +1,43 @@ +{% extends 'users/sidebar.html' %} +{% comment %} +Re2o est un logiciel d'administration développé initiallement au rezometz. Il +se veut agnostique au réseau considéré, de manière à être installable en +quelques clics. + +Copyright © 2017 Gabriel Détraz +Copyright © 2017 Lara Kermarec +Copyright © 2017 Augustin Lemesle + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +{% endcomment %} + +{% load bootstrap3 %} +{% load i18n %} + +{% block title %}{% trans "Deletion of tickets" %}{% endblock %} + +{% block content %} + +
+ {% csrf_token %} +

{% blocktrans %}Warning: are you sure you want to delete this {{ objet_name }} object ( {{ objet }} )?{% endblocktrans %}

+ {% trans "Confirm" as tr_confirm %} + {% bootstrap_button tr_confirm button_type="submit" icon="trash" button_class='btn-danger' %} +
+
+
+
+{% endblock %} + diff --git a/tickets/templates/tickets/publication_mail_en b/tickets/templates/tickets/publication_mail_en index 1e436877..97ad4b2c 100644 --- a/tickets/templates/tickets/publication_mail_en +++ b/tickets/templates/tickets/publication_mail_en @@ -1,12 +1,12 @@ {% if ticket.user %} {{ ticket.user.get_full_name }} opened a ticket. Profile: {{site_url}}{% url 'users:profil' ticket.user.id%} -Answer to the address: {{ticket.user.get_mail}}. {% else %} An anonymous user (not authenticated) opened a ticket -Answer to the address:{{ticket.email}}. {% endif %} +Answer to the address: {{{ticket.get_mail}}. Title: {{ ticket.title | safe }} Description: {{ ticket.description | safe }} + diff --git a/tickets/templates/tickets/publication_mail_fr b/tickets/templates/tickets/publication_mail_fr index 3b733e8f..3491a8d8 100644 --- a/tickets/templates/tickets/publication_mail_fr +++ b/tickets/templates/tickets/publication_mail_fr @@ -1,11 +1,11 @@ {% if ticket.user %} {{ ticket.user.get_full_name }} a ouvert un ticket. Profil : {{site_url}}{% url 'users:profil' ticket.user.id%} -Répondre à l'adresse : {{ticket.user.get_mail}}. {% else %} Un utilisateur anonyme (non connecté) a ouvert un ticket. -Répondre à l'adresse : {{ticket.email}}. {% endif %} +Répondre à l'adresse : {{ticket.get_mail}}. + Titre : {{ ticket.title | safe }} diff --git a/tickets/templates/tickets/update_mail_en b/tickets/templates/tickets/update_mail_en new file mode 100644 index 00000000..8c25d4fd --- /dev/null +++ b/tickets/templates/tickets/update_mail_en @@ -0,0 +1,12 @@ +Hello, + +The ticket {{ comment.parent_ticket.title | safe }} n°{{ comment.parent_ticket.id }}, opened by {{ comment.parent_ticket.opened_by }}, has been updated by {{ comment.created_by.get_full_name | safe }}. +{% if comment.parent_ticket.user %} +The complete re2o profil can be found here : {{site_url}}{% url 'users:profil' comment.parent_ticket.user.id%} +{% endif %} + +Description : {{ comment.comment | safe }} + +Best regards, + +The member of the association diff --git a/tickets/templates/tickets/update_mail_fr b/tickets/templates/tickets/update_mail_fr new file mode 100644 index 00000000..3b1ec8ee --- /dev/null +++ b/tickets/templates/tickets/update_mail_fr @@ -0,0 +1,12 @@ +Bonjour, + +Le ticket {{ comment.parent_ticket.title | safe }} n°{{ comment.parent_ticket.id }}, ouvert par {{ comment.parent_ticket.opened_by }}, a reçu une mise à jour par {{ comment.created_by.get_full_name | safe }}. +{% if comment.parent_ticket.user %} +Le profil re2o est accessible à l'adresse suivante : {{site_url}}{% url 'users:profil' comment.parent_ticket.user.id%} +{% endif %} + +Description : {{ comment.comment | safe }} + +Cordialement, + +Les membres actifs de l'association diff --git a/tickets/urls.py b/tickets/urls.py index dee41a14..fe0b88ec 100644 --- a/tickets/urls.py +++ b/tickets/urls.py @@ -39,4 +39,7 @@ urlpatterns = [ name="edit-options", ), url(r"^new_ticket/$", views.new_ticket, name="new-ticket"), + url(r"^add_comment/(?P[0-9]+)$", views.add_comment, name="add-comment"), + url(r"^edit_comment/(?P[0-9]+)$", views.edit_comment, name="edit-comment"), + url(r"^del_comment/(?P[0-9]+)$", views.del_comment, name="del-comment"), ] diff --git a/tickets/views.py b/tickets/views.py index f8342476..bda14df7 100644 --- a/tickets/views.py +++ b/tickets/views.py @@ -36,13 +36,19 @@ from re2o.views import form from re2o.base import re2o_paginator -from re2o.acl import can_view, can_view_all, can_edit, can_create +from re2o.acl import ( + can_view, + can_view_all, + can_edit, + can_create, + can_delete +) from preferences.models import GeneralOption -from .models import Ticket +from .models import Ticket, CommentTicket -from .forms import NewTicketForm, EditTicketForm +from .forms import NewTicketForm, EditTicketForm, CommentTicketForm def new_ticket(request): @@ -71,10 +77,11 @@ def new_ticket(request): @can_view(Ticket) def aff_ticket(request, ticket, ticketid): """View to display only one ticket""" + comments = CommentTicket.objects.filter(parent_ticket=ticket) return render( request, "tickets/aff_ticket.html", - {"ticket": ticket}, + {"ticket": ticket, "comments": comments}, ) @@ -110,6 +117,59 @@ def edit_ticket(request, ticket, ticketid): ) +@login_required +@can_view(Ticket) +def add_comment(request, ticket, ticketid): + """ Add a comment to a ticket""" + commentticket = CommentTicketForm(request.POST or None) + if commentticket.is_valid(): + commentticket = commentticket.save(commit=False) + commentticket.parent_ticket = ticket + commentticket.created_by = request.user + commentticket.save() + messages.success(request, _("This comment was added.")) + return redirect( + reverse("tickets:aff-ticket", kwargs={"ticketid": str(ticketid)}) + ) + return form( + {"ticketform": commentticket, "action_name": _("Add a comment")}, "tickets/edit.html", request + ) + + +@login_required +@can_edit(CommentTicket) +def edit_comment(request, commentticket_instance, **_kwargs): + """ Edit a comment of a ticket""" + commentticket = CommentTicketForm(request.POST or None, instance=commentticket_instance) + if commentticket.is_valid(): + ticketid = commentticket_instance.parent_ticket.id + if commentticket.changed_data: + commentticket.save() + messages.success(request, _("This comment was edited.")) + return redirect( + reverse("tickets:aff-ticket", kwargs={"ticketid": str(ticketid)}) + ) + return form( + {"ticketform": commentticket, "action_name": _("Edit")}, "tickets/edit.html", request, + ) + + +@login_required +@can_delete(CommentTicket) +def del_comment(request, commentticket, **_kwargs): + """Delete a comment of a ticket""" + if request.method == "POST": + ticketid = commentticket.parent_ticket.id + commentticket.delete() + messages.success(request, _("The comment was deleted.")) + return redirect( + reverse("tickets:aff-ticket", kwargs={"ticketid": str(ticketid)}) + ) + return form( + {"objet": commentticket, "objet_name": _("Ticket Comment")}, "tickets/delete.html", request + ) + + @login_required @can_view_all(Ticket) def aff_tickets(request):