mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-09-12 01:03:09 +00:00
Add comment to tikets + send_notif mails
This commit is contained in:
parent
c8c89fab1d
commit
38b119bb8e
15 changed files with 524 additions and 76 deletions
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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 <laouen.fernet@supelec.fr>\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"
|
||||
|
||||
|
|
40
tickets/migrations/0005_auto_20200422_2309.py
Normal file
40
tickets/migrations/0005_auto_20200422_2309.py
Normal file
|
@ -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'),
|
||||
),
|
||||
]
|
31
tickets/migrations/0006_auto_20200423_0202.py
Normal file
31
tickets/migrations/0006_auto_20200423_0202.py
Normal file
|
@ -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,
|
||||
),
|
||||
]
|
20
tickets/migrations/0007_ticket_language.py
Normal file
20
tickets/migrations/0007_ticket_language.py
Normal file
|
@ -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),
|
||||
),
|
||||
]
|
|
@ -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()
|
||||
|
|
|
@ -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: " %}<A HREF="mailto:{{ticket.email}}?subject={% trans "Response to your ticket"%}">{{ticket.email}}</A>
|
||||
{% endif %}
|
||||
|
||||
<div class="text-right">
|
||||
{% can_view ticket %}
|
||||
<a class="btn btn-info btn-sm" role="button" href="{% url 'tickets:add-comment' ticket.id %}"><i class="fa fa-plus"></i> {% trans "Add a comment " %}</a>
|
||||
{% acl_end %}
|
||||
{% can_edit ticket %}
|
||||
<a class="btn btn-info btn-sm" role="button" href="{% url 'tickets:edit-ticket' ticket.id %}"><i class="fa fa-edit"></i> {% trans "Edit" %}</a>
|
||||
{% if not ticket.solved %}
|
||||
<a class="btn btn-success btn-sm" role="button" href="{% url 'tickets:change-ticket-status' ticket.id %}"><i class="fa fa-check"></i> {% trans "Mark as solved" %}</a>
|
||||
{% else %}
|
||||
<a class="btn btn-warning btn-sm" role="button" href="{% url 'tickets:change-ticket-status' ticket.id %}"><i class="fa fa-close"></i> {% trans "Mark as unsolved" %}</a>
|
||||
{% endif %}
|
||||
{% acl_end %}
|
||||
{% history_button ticket text=True %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
||||
<p><b>{% trans "Title:" %}</b> {{ticket.title}}</p>
|
||||
<p><b>{% trans "Description:" %}</b> {{ ticket.description | linebreaks }}</p>
|
||||
<b>{% trans "Description:" %}</b> {{ ticket.description | linebreaks }}
|
||||
|
||||
<div class="text-right">
|
||||
{% can_edit ticket %}
|
||||
<a type="button" href="{% url 'tickets:edit-ticket' ticket.id %}" class="btn btn-info"><p>{% trans "Edit this ticket" %}</p></a>
|
||||
{% if not ticket.solved %}
|
||||
<a type="button" href="{% url 'tickets:change-ticket-status' ticket.id %}" class="btn btn-success"><p>{% trans "Mark as solved" %}</p></a>
|
||||
{% else %}
|
||||
<a type="button" href="{% url 'tickets:change-ticket-status' ticket.id %}" class="btn btn-warning"><p>{% trans "Mark as unsolved" %}</p></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% for comment in comments %}
|
||||
<div class="panel-footer">
|
||||
<p><span class="badge">{% trans "Comment " %}<b>#{{comment.comment_id}}</b></span> {% trans " added by " %}{{ comment.created_by.get_full_name }}{% trans " on " %} {{comment.created_at}}</p>
|
||||
<b>{% trans "Description:" %}</b> {{ comment.comment | linebreaks }}
|
||||
<div class="text-right">
|
||||
{% can_edit comment %}
|
||||
<a class="btn btn-info btn-sm" role="button" href="{% url 'tickets:edit-comment' comment.id %}"><i class="fa fa-edit"></i> {% trans "Edit this comment " %}</a>
|
||||
{% acl_end %}
|
||||
{% can_delete comment %}
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'tickets:del-comment' comment.id %}"><i class="fa fa-close"></i> {% trans "Delete this comment " %}</a>
|
||||
{% acl_end %}
|
||||
{% history_button comment text=True %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="text-right">
|
||||
<a type="button" href="{% url 'tickets:aff-tickets' %}" class="btn btn-primary"><p>{% trans "All tickets" %}</p></a>
|
||||
<a class="btn btn-primary" role="button" href="{% url 'tickets:aff-tickets' %}"><i class="fa fa-reorder"></i> {% trans "All tickets" %}</a>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
43
tickets/templates/tickets/delete.html
Normal file
43
tickets/templates/tickets/delete.html
Normal file
|
@ -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 %}
|
||||
|
||||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
<h4>{% blocktrans %}Warning: are you sure you want to delete this {{ objet_name }} object ( {{ objet }} )?{% endblocktrans %}</h4>
|
||||
{% trans "Confirm" as tr_confirm %}
|
||||
{% bootstrap_button tr_confirm button_type="submit" icon="trash" button_class='btn-danger' %}
|
||||
</form>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
{% endblock %}
|
||||
|
|
@ -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 }}
|
||||
|
||||
|
|
|
@ -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 }}
|
||||
|
||||
|
|
12
tickets/templates/tickets/update_mail_en
Normal file
12
tickets/templates/tickets/update_mail_en
Normal file
|
@ -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
|
12
tickets/templates/tickets/update_mail_fr
Normal file
12
tickets/templates/tickets/update_mail_fr
Normal file
|
@ -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
|
|
@ -39,4 +39,7 @@ urlpatterns = [
|
|||
name="edit-options",
|
||||
),
|
||||
url(r"^new_ticket/$", views.new_ticket, name="new-ticket"),
|
||||
url(r"^add_comment/(?P<ticketid>[0-9]+)$", views.add_comment, name="add-comment"),
|
||||
url(r"^edit_comment/(?P<commentticketid>[0-9]+)$", views.edit_comment, name="edit-comment"),
|
||||
url(r"^del_comment/(?P<commentticketid>[0-9]+)$", views.del_comment, name="del-comment"),
|
||||
]
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in a new issue