8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-12-23 07:23:46 +00:00

Merge branch 'tickets' into 'dev'

Tickets

See merge request federez/re2o!427
This commit is contained in:
grizzly 2019-08-21 13:14:12 +02:00
commit 7f07b1ed48
35 changed files with 904 additions and 4 deletions

View file

@ -503,5 +503,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
</div>
</div>
{% for template in optionnal_templates_list %}
{{ template }}
{% endfor %}
{% endblock %}

View file

@ -40,6 +40,8 @@ from django.utils.translation import ugettext as _
from reversion import revisions as reversion
from importlib import import_module
from re2o.settings_local import OPTIONNAL_APPS_RE2O
from re2o.views import form
from re2o.acl import can_create, can_edit, can_delete_set, can_view_all, can_delete
@ -94,6 +96,10 @@ def display_options(request):
radiusoptions, _ = RadiusOption.objects.get_or_create()
cotisationsoptions, _created = CotisationsOption.objects.get_or_create()
document_template_list = DocumentTemplate.objects.order_by('name')
optionnal_apps = [import_module(app) for app in OPTIONNAL_APPS_RE2O]
optionnal_templates_list = [app.views.preferences(request) for app in optionnal_apps]
return form({
'useroptions': useroptions,
'machineoptions': machineoptions,
@ -109,6 +115,7 @@ def display_options(request):
'switchmanagementcred_list': switchmanagementcred_list,
'radiusoptions' : radiusoptions,
'cotisationsoptions': cotisationsoptions,
'optionnal_templates_list': optionnal_templates_list,
'document_template_list': document_template_list,
}, 'preferences/display_preferences.html', request)

View file

@ -29,7 +29,8 @@ from django.contrib import messages
from django.http import HttpRequest
from preferences.models import GeneralOption, OptionalMachine
from django.utils.translation import get_language
from importlib import import_module
from re2o.settings_local import OPTIONNAL_APPS_RE2O
def context_user(request):
"""Fonction de context lorsqu'un user est logué (ou non),
@ -57,6 +58,15 @@ def context_user(request):
'ipv6_enabled': OptionalMachine.get_cached_value('ipv6'),
}
def context_optionnal_apps(request):
"""Fonction de context pour générer la navbar en fonction des
apps optionnels"""
optionnal_apps = [import_module(app) for app in OPTIONNAL_APPS_RE2O]
optionnal_templates_navbar_user_list = [app.views.navbar_user(request) for app in optionnal_apps]
optionnal_templates_navbar_logout_list = [app.views.navbar_logout(request) for app in optionnal_apps]
return {'optionnal_templates_navbar_user_list':optionnal_templates_navbar_user_list,
'optionnal_templates_navbar_logout_list':optionnal_templates_navbar_logout_list}
def date_now(request):
"""Add the current date in the context for quick informations and

View file

@ -62,6 +62,7 @@ DJANGO_CONTRIB_APPS = (
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.humanize',
)
EXTERNAL_CONTRIB_APPS = (
'bootstrap3',
@ -131,6 +132,7 @@ TEMPLATES = [
'django.contrib.messages.context_processors.messages',
'django.template.context_processors.request',
're2o.context_processors.context_user',
're2o.context_processors.context_optionnal_apps',
're2o.context_processors.date_now',
],
},

View file

@ -108,5 +108,8 @@ GID_RANGES = {
'posix': [501, 600],
}
# Some optionnal Re2o Apps
OPTIONNAL_APPS_RE2O = ()
# Some Django apps you want to add in you local project
OPTIONNAL_APPS = ()
OPTIONNAL_APPS = OPTIONNAL_APPS_RE2O + ()

View file

@ -31,6 +31,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% block content %}
<h2>{% blocktrans %}Contact the organisation {{asso_name}}{% endblocktrans %}</h2>
</br>
{% for template in optionnal_templates_contact_list %}
{{template}}
{% endfor %}
{% for contact in contacts %}

View file

@ -49,6 +49,8 @@ from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from django.views.generic import RedirectView
from .settings_local import OPTIONNAL_APPS_RE2O
from .views import index, about_page, contact_page
# Admin site configuration
@ -83,6 +85,10 @@ urlpatterns = [
url(r'^admin/login/$', RedirectView.as_view(pattern_name='login')),
url(r'^admin/', include(admin.site.urls)),
]
urlpatterns += [url(r'^{}/'.format(app), include('{}.urls'.format(app), namespace=app)) for app in OPTIONNAL_APPS_RE2O]
# Add debug_toolbar URLs if activated
if 'debug_toolbar' in settings.INSTALLED_APPS:
import debug_toolbar

View file

@ -43,6 +43,8 @@ from preferences.models import (
)
from .contributors import CONTRIBUTORS
from importlib import import_module
from re2o.settings_local import OPTIONNAL_APPS_RE2O
def form(ctx, template, request):
@ -113,12 +115,16 @@ def contact_page(request):
"""
address = MailContact.objects.all()
optionnal_apps = [import_module(app) for app in OPTIONNAL_APPS_RE2O]
optionnal_templates_contact_list = [app.views.contact(request) for app in optionnal_apps]
return render(
request,
"re2o/contact.html",
{
'contacts': address,
'asso_name': AssoOption.objects.first().name
'asso_name': AssoOption.objects.first().name,
'optionnal_templates_contact_list':optionnal_templates_contact_list,
}
)

View file

@ -100,6 +100,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% can_view_app cotisations %}
<li><a href="{% url 'cotisations:index' %}"><i class="fa fa-eur"></i> {% trans "Manage the subscriptions" %}</a></li>
{% acl_end %}
{% for template in optionnal_templates_navbar_user_list%}
{{ template }}
{% endfor %}
</ul>
</li>
{% acl_end %}
@ -126,13 +130,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><i class="fa fa-info"></i> {% trans "More information" %}<span class="caret"></span></a>
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><i class="fa fa-info"></i> {% trans "Information and contact" %}<span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="{% url 'about' %}"><i class="fa fa-info-circle"></i> {% trans "About" %}</a></li>
<li><a href="{% url 'contact' %}"><i class="fa fa-at"></i> {% trans "Contact" %}</a></li>
{% comment %}
<li><a href="{% url 'tickets:new-ticket' %}"><i class="fa fa-ticket"></i> {% trans "Ouvrir un ticket" %}</a><li>
{% endcomment %}
</ul>
</li>
{% if not request.user.is_authenticated %}
{% for template in optionnal_templates_navbar_logout_list %}
{{ template }}
{% endfor %}
{% if var_sa %}
<li>
<a href="{% url 'users:new-user' %}">

0
tickets/__init__.py Normal file
View file

4
tickets/admin.py Normal file
View file

@ -0,0 +1,4 @@
from django.contrib import admin
from .models import Ticket
admin.site.register(Ticket)
# Register your models here.

5
tickets/apps.py Normal file
View file

@ -0,0 +1,5 @@
from django.apps import AppConfig
class TicketsConfig(AppConfig):
name = 'tickets'

24
tickets/forms.py Normal file
View file

@ -0,0 +1,24 @@
from django import forms
from django.forms import ModelForm, Form
from re2o.field_permissions import FieldPermissionFormMixin
from re2o.mixins import FormRevMixin
from django.utils.translation import ugettext_lazy as _
from .models import(
Ticket,
)
class NewTicketForm(ModelForm):
""" Creation of a ticket"""
email = forms.EmailField(required=False)
class Meta:
model = Ticket
fields = ['title', 'description', 'email']
class ChangeStatusTicketForm(ModelForm):
""" Change ticket status"""
class Meta:
model = Ticket
fields = []

View file

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2019-08-19 08:19
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import re2o.mixins
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Preferences',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('publish_address', models.EmailField(help_text='Email address to publish the new tickets (leave empty for no publications)', max_length=1000, null=True)),
('mail_language', models.IntegerField(choices=[(0, 'Français'), (1, 'English')], default=0)),
],
options={
'verbose_name': "Ticket's settings",
},
),
migrations.CreateModel(
name='Ticket',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(help_text='Title of the ticket', max_length=255)),
('description', models.TextField(help_text='Description of the ticket', max_length=3000)),
('date', models.DateTimeField(auto_now_add=True)),
('email', models.EmailField(help_text='An email address to get back to you', max_length=100, null=True)),
('solved', models.BooleanField(default=False)),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='tickets', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Ticket',
'verbose_name_plural': 'Tickets',
},
bases=(re2o.mixins.AclMixin, models.Model),
),
]

View file

97
tickets/models.py Normal file
View file

@ -0,0 +1,97 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.core.mail import send_mail
from django.template import Context, loader
from django.db.models.signals import post_save
from django.dispatch import receiver
from re2o.mixins import AclMixin
from preferences.models import GeneralOption
import users.models
from .preferences.models import Preferences
class Ticket(AclMixin, models.Model):
"""Model of a ticket"""
user = models.ForeignKey(
'users.User',
on_delete=models.CASCADE,
related_name="tickets",
blank=True,
null=True)
title = models.CharField(
max_length=255,
help_text=_("Title of the ticket"),
blank=False,
null=False,)
description = models.TextField(
max_length=3000,
help_text=_("Description of the ticket"),
blank=False,
null=False)
date = models.DateTimeField(auto_now_add=True)
email = models.EmailField(
help_text = _("An email address to get back to you"),
max_length=100,
null=True)
solved = models.BooleanField(default=False)
class Meta:
verbose_name = _("Ticket")
verbose_name_plural = _("Tickets")
def __str__(self):
if self.user:
return "Ticket from {}. Date: {}".format(self.user.surname,self.date)
else:
return "Anonymous Ticket. Date: {}".format(self.date)
def publish_mail(self):
site_url = GeneralOption.objects.first().main_site_url
to_addr = Preferences.objects.first().publish_address
context = Context({'ticket':self,'site_url':site_url})
lang = Preferences.objects.first().mail_language
if(lang == 0):
obj = 'Nouvelle ouverture de ticket'
template = loader.get_template('tickets/publication_mail_fr')
else:
obj = 'New ticket opened'
template = loader.get_template('tickets/publication_mail_en')
send_mail(
obj,
template.render(context),
GeneralOption.get_cached_value('email_from'),
[to_addr],
fail_silently = False)
def can_view(self, user_request, *_args, **_kwargs):
""" 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_ticket') and self.user != user_request):
return False, _("You don't have the right to view other tickets than yours.")
else:
return True, None
@staticmethod
def can_view_all(user_request, *_args, **_kwargs):
""" Check that the user has access to the list of all tickets"""
return(
user_request.has_perm('tickets.view_tickets'),
_("You don't have the right to view the list of tickets.")
)
def can_create(user_request,*_args, **_kwargs):
""" Authorise all users to open tickets """
return True,None
@receiver(post_save, sender=Ticket)
def ticket_post_save(**kwargs):
""" Send the mail to publish the new ticket """
if kwargs['created']:
if Preferences.objects.first().publish_address:
ticket = kwargs['instance']
ticket.publish_mail()

View file

@ -0,0 +1,11 @@
from django import forms
from django.forms import ModelForm, Form
from django.utils.translation import ugettext_lazy as _
from .models import Preferences
class EditPreferencesForm(ModelForm):
""" Edit the ticket's settings"""
class Meta:
model = Preferences
fields = '__all__'

View file

@ -0,0 +1,19 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _
class Preferences(models.Model):
""" Definition of the ticket's settings"""
publish_address = models.EmailField(
help_text = _("Email address to publish the new tickets (leave empty for no publications)"),
max_length = 1000,
null = True)
LANG_FR = 0
LANG_EN = 1
LANGUES = (
(0,_("Français")),
(1,_("English")),
)
mail_language = models.IntegerField(choices=LANGUES,default = LANG_FR)
class Meta:
verbose_name = _("Ticket's settings")

View file

@ -0,0 +1,82 @@
{% 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 Goulven 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 %}
{% load humanize %}
{% block title %}{% trans "Tickets" %}{% endblock %}
{% block content %}
<h2> Ticket #{{ticket.id}}
{% if ticket.solved %}
<span class="badge badge-success">{% trans "Solved" %}</span>
{% else %}
<span class="badge badge-danger">{% trans "Not Solved" %}</span>
{% endif %}
</h2>
<div class="panel panel-default">
<div class="panel-heading">
{% trans "Opened by" %}
{% if ticket.user %}
<a href="{% url 'users:profil' ticket.user.id%}">
{{ ticket.user.get_full_name }}
</a>
{% else %}
{% trans "Anonymous User" %}
{% endif %}
{{ ticket.date | naturalday}}.
{% if not ticket.user %}
{% trans "Response address: " %}<A HREF="mailto:{{ticket.email}}?subject={% trans "Response to your ticket"%}">{{ticket.email}}</A>
{% endif %}
</div>
<div class="panel-body">
<p><b>{% trans "Title:" %}</b> {{ticket.title}}</p>
<p><b>{% trans "Description" %}</b> {{ ticket.description }}</p>
<div class="text-right">
<form class="form" method="post">
{% csrf_token %}
{% bootstrap_form changestatusform %}
{% if not ticket.solved %}
{% bootstrap_button "Mark as Solved" button_type="submit" button_class='btn-info' %}
{% else %}
{% bootstrap_button "Mark as not Solved" button_type="submit" button_class='btn-warning' %}
{% endif %}
</form>
</div>
</div>
</div>
<div class="text-right">
<a type="button" href="{% url 'tickets:aff-tickets' %}" class="btn btn-primary"><p>{% trans "Tous les tickets" %}</p></a>
</div>
{% endblock %}

View file

@ -0,0 +1,89 @@
{% 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 Goulven 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 content %}
<div class="container-fluid">
<hr class="col-sm-12">
<div class="row justify-content-start">
<div class="col-sm-4">
<span class="badge badge-light">{{ nbr_tickets }}</span> {% trans "Tickets" %}
</div>
<div class="col-sm-4">
<span class="badge badge-light"> {{ nbr_tickets_unsolved }}</span>{% trans "Not Solved Tickets" %}
</div>
<div class="col-sm-4">
<span>{% trans "Last Ticket:" %} {{ last_ticket_date }}</span>
</div>
</div>
<hr class="col-sm-12">
</div>
<div class="table-responsiv">
<table class="table">
<thead>
<tr>
<th scope="col"></th>
<th scope="col">User</th>
<th scope="col">Titre</th>
<th scope="col">Date</th>
<th scope="col">Résolu</th>
</tr>
{% for ticket in tickets_list %}
<tr>
<td>
<a href="{% url 'tickets:aff-ticket' ticket.id%}" class="btn btn-primary btn-sm" role="button">
<i class="fa fa-ticket"></i>
</a>
</td>
{% if ticket.user %}
<td><a href="{% url 'users:profil' ticket.user.id%}" role="button">{{ ticket.user.get_short_name }}</a></td>
{% else %}
<td> Anonyme </td>
{% endif %}
<td>{{ ticket.title }}</td>
<td>{{ ticket.date }}</td>
{% if ticket.solved %}
<td><i class="fa fa-check" style="color:green"></i></td>
{% else %}
<td><i class="fa fa-times" style="color:red"></i></td>
{% endif %}
</tr>
{% endfor %}
</thead>
</table>
{% if tickets_list.paginator %}
{% include 'pagination.html' with list=tickets_list go_to_id="tickets" %}
{% endif %}
</div>
{% endblock %}

View file

@ -0,0 +1,13 @@
{% load i18n %}
<div class="panel panel-info">
<div class="panel-heading"><h4>Tickets</h4></div>
<div class="panel-body">
<div class="row">
<div class="col-sm-9">
{% blocktrans %}If you are experiencing issues with the services offered by {{asso_name}}, you can open a ticket that will be taken care of. If you want to contact us on any other topic, please choose one address below.{% endblocktrans %}
</div>
<div class="col-sm-3"><a class="btn btn-primary" href="{% url 'tickets:new-ticket' %}"><i class="fa fa-ticket"></i> {% trans "Ouvrir un ticket" %}</a></div>
</div>
</div>
</div>

View file

@ -0,0 +1,48 @@
{% extends 'machines/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 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
Copyright © 2017 Maël Kervella
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 "Ticket" %}{% endblock %}
{% block content %}
<h2> {% trans "Tickets settings modification" %}</h2>
{% for message in messages %}
<div class="{{ message| bootstrap_message_classes }} alert-dismissable">
<button type="button" class="close" data_dismiss="alert" aria-hidden="true">&#125;</button>
{{ message | safe }}
</div>
{% endfor %}
<form class="form" method="post">
{% csrf_token %}
{% bootstrap_field preferencesform.publish_address %}
{% bootstrap_field preferencesform.mail_language %}
{% bootstrap_button "Editer" button_type="submit" icon='ok' button_class='btn-success' %}
</form>
{% endblock %}

View file

@ -0,0 +1,58 @@
{% extends 'machines/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 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
Copyright © 2017 Maël Kervella
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 massive_bootstrap_form %}
{% load i18n %}
{% block title %}{% trans "Ticket" %}{% endblock %}
{% block content %}
<h2> Ouverture d'un Ticket </h2>
<form class="form" method="post">
{% csrf_token %}
{% if not user.is_authenticated %}
<p>{% trans "Vous n'êtes pas authentifié. Veuillez fournir une adresse mail afin que nous puissions vous recontacter." %}</p>
{% bootstrap_field ticketform.email %}
{% endif %}
{% bootstrap_field ticketform.title %}
<br>
<p>{% trans "Description de votre problème. Veuillez fournir le plus d'informations possible afin de faciliter la recherche de solution. Voici quelques informations dont nous pourions avoir besoin:" %}</p>
<ul class="list">
<li>
<p> {% trans "Le type de votre problème (adhesion, connexion, paiement ou autre)." %}</p>
</li>
<li>
<p> {% trans "Les conditions dans lesquelles vous rencontrez le problème (Wifi/filaire, sur tout les apareils ou sur un seul. Est-ce une nouvelle machine ?" %}</p>
</li>
<li>
<p> {% trans "Les endroits dans lequels le problème survient (chez vous, dans une partie commune, dans un batiment en particulier)." %}</p>
</ul>
{% bootstrap_field ticketform.description %}
{% bootstrap_button "Ouvrir le Ticket" button_type="submit" icon='ok' button_class='btn-success' %}
</form>
{% endblock %}

View file

@ -0,0 +1,34 @@
{% 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 Goulven 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 "Tickets" %}{% endblock %}
{% block content %}
<h2>{% trans "Tickets" %}</h2>
{% include 'tickets/aff_tickets.html' with tickets_list=tickets_list %}
{% endblock %}

View file

@ -0,0 +1,2 @@
{% load i18n %}
<li><a href="{% url 'tickets:aff-tickets' %}"><i class="fa fa-ticket"></i>{% trans "Tickets" %}</a></li>

View file

@ -0,0 +1,6 @@
{% load i18n %}
<li>
<a href="{% url 'tickets:new-ticket' %}">
<i class="fa fa-ticket"></i> {% trans "Ouvrir un ticket" %}
</a>
</li>

View file

@ -0,0 +1,36 @@
{% load i18n %}
<div class="panel panel-default" id="tickets">
<div class="panel-heading" data-toggle="collapse" href="#collapse_tickets">
<h4 class="panel-title">
<a><i class="fa fa-ticket"></i> {% trans "Tickets" %}</a>
</h4>
</div>
<div id="collapse_tickets" class="panel-collapse panel-body collapse">
<a class="btn btn-primary btn-sm" role="button" href="{% url 'tickets:edit-preferences-tickets' %}">
<i class="fa fa-edit"></i>
{% trans "Edit" %}
</a>
<p></p>
<div class="table-responsive">
<table class="table">
<tr>
<th><p>{% trans "Publication email address"%}</p></th>
{% if preferences.publish_address %}
<td><p>{{ preferences.publish_address }}</p></td>
{% else %}
<td><p>{% trans "Pas d'adresse, les tickets ne sont pas annoncés" %}</p></td>
{% endif %}
</tr>
<tr>
<th><p>{% trans "Email language" %}</p></th>
<td><p>{{ language }}</p></th>
</tr>
<table class="table">
</table>
</div>
</div>
</div>

View file

@ -0,0 +1,23 @@
{% load i18n %}
<div class="panel panel-default">
<div class="panel-heading clearfix profil" data-parent="#accordion" data-toggle="collapse" data-target="#ticket">
<h3 class="panel-title pull-left">
<i class="fa fa-ticket"></i>{% trans " Tickets" %}
</h3>
</div>
<div id="ticket" class="panel-collapse collapse">
<div class="panel-body">
<a class="btn btn-primary btn-sm" role="button" href="{% url 'tickets:new-ticket' %}">
<i class="fa fa-ticket"></i>{% trans " Open a Ticket" %}
</a>
</div>
<div class="panel-body">
{% if tickets_list %}
{% include 'tickets/aff_tickets.html' with tickets_list=tickets_list %}
{% else %}
<p>{% trans "No tickets" %}</p>
{% endif %}
</div>
</div>
</div>

View file

@ -0,0 +1,12 @@
{% if ticket.user %} {{ ticket.user.get_full_name }} opened a ticket.
Profil: {{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 %}
Title: {{ticket.title}}
Description: {{ticket.description}}

View file

@ -0,0 +1,12 @@
{% if ticket.user %} {{ ticket.user.get_full_name }} à ouvert un ticket.
Profile: {{site_url}}{% url 'users:profil' ticket.user.id%}
Répondre à l'adresse: {{ticket.user.get_mail}}.
{% else %}
Un utilisateur anonyme (non connecté) à ouvert un ticket.
Répondre à l'adresse: {{ticket.email}}.
{% endif %}
Titre: {{ticket.title}}
Description: {{ticket.description}}

3
tickets/tests.py Normal file
View file

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

10
tickets/urls.py Normal file
View file

@ -0,0 +1,10 @@
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.aff_tickets, name='aff-tickets'),
url(r'^ticket/(?P<ticketid>[0-9]+)$', views.aff_ticket, name='aff-ticket'),
url(r'^ticket/edit-preferences-tickets$', views.edit_preferences, name='edit-preferences-tickets'),
url(r'^new_ticket/$',views.new_ticket,name='new-ticket'),
]

197
tickets/views.py Normal file
View file

@ -0,0 +1,197 @@
# -*- mode: python; coding: utf-8 -*-
# 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 Goulven 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.
# App de gestion des users pour re2o
# Goulven Kermarec, Gabriel Détraz, Lemesle Augustin
# Gplv2
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
from django.template.loader import render_to_string
from django.views.decorators.cache import cache_page
from django.utils.translation import ugettext as _
from django.urls import reverse
from django.forms import modelformset_factory
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 preferences.models import GeneralOption
from .models import(
Ticket,
)
from .preferences.models import(
Preferences,
)
from .forms import (
NewTicketForm,
ChangeStatusTicketForm,
)
from .preferences.forms import (
EditPreferencesForm,
)
def new_ticket(request):
""" Ticket creation view"""
ticketform = NewTicketForm(request.POST or None)
if request.method == 'POST':
ticketform = NewTicketForm(request.POST)
if ticketform.is_valid():
email = ticketform.cleaned_data.get('email')
ticket = ticketform.save(commit=False)
if request.user.is_authenticated:
ticket.user = request.user
ticket.save()
messages.success(request,_('Your ticket has been succesfully open. We will take care of it as soon as possible.'))
return redirect(reverse('users:profil',kwargs={'userid':str(request.user.id)}))
if not request.user.is_authenticated and email != "":
ticket.save()
messages.success(request,_('Your ticket has been succesfully open. We will take care of it as soon as possible.'))
return redirect(reverse('index'))
else:
messages.error(request,_("You are not authenticated. Please login or provide an email address so we can get back to you."))
return form({'ticketform':ticketform,},'tickets/form_ticket.html',request)
else:
ticketform = NewTicketForm
return form({'ticketform':ticketform,},'tickets/form_ticket.html',request)
@login_required
@can_view(Ticket)
def aff_ticket(request, ticket, ticketid):
"""View to display only one ticket"""
changestatusform = ChangeStatusTicketForm(request.POST)
if request.method == 'POST':
ticket.solved = not ticket.solved
ticket.save()
return render(request,'tickets/aff_ticket.html',{'ticket':ticket,'changestatusform':changestatusform})
@login_required
@can_view_all(Ticket)
def aff_tickets(request):
""" View to display all the tickets """
tickets_list = Ticket.objects.all().order_by('-date')
nbr_tickets = tickets_list.count()
nbr_tickets_unsolved = tickets_list.filter(solved=False).count()
if nbr_tickets:
last_ticket_date = tickets_list.first().date
else:
last_ticket_date = _("Never")
pagination_number = (GeneralOption
.get_cached_value('pagination_number'))
tickets = re2o_paginator(
request,
tickets_list,
pagination_number,
)
context = {'tickets_list':tickets,
'last_ticket_date':last_ticket_date,
'nbr_tickets':nbr_tickets,
'nbr_tickets_unsolved':nbr_tickets_unsolved}
return render(request,'tickets/index.html',context=context)
def edit_preferences(request):
""" View to edit the settings of the tickets """
preferences_instance, created = Preferences.objects.get_or_create(id=1)
preferencesform = EditPreferencesForm(
request.POST or None,
instance = preferences_instance,)
if preferencesform.is_valid():
if preferencesform.changed_data:
preferencesform.save()
messages.success(request,'Préférences des Tickets mises à jour')
return redirect(reverse('preferences:display-options',))
else:
messages.error(request,'Formulaire Invalide')
return form({'preferencesform':preferencesform,},'tickets/form_preferences.html',request)
return form({'preferencesform':preferencesform,},'tickets/form_preferences.html',request)
# views cannoniques des apps optionnels
def profil(request,user):
""" View to display the ticket's module on the profil"""
tickets_list = Ticket.objects.filter(user=user).all().order_by('-date')
nbr_tickets = tickets_list.count()
nbr_tickets_unsolved = tickets_list.filter(solved=False).count()
if nbr_tickets:
last_ticket_date = tickets_list.first().date
else:
last_ticket_date = _("Never")
pagination_number = (GeneralOption
.get_cached_value('pagination_large_number'))
tickets = re2o_paginator(
request,
tickets_list,
pagination_number,
)
context = {'tickets_list':tickets,
'last_ticket_date':last_ticket_date,
'nbr_tickets':nbr_tickets,
'nbr_tickets_unsolved':nbr_tickets_unsolved}
return render_to_string('tickets/profil.html', context=context, request=request, using=None)
def preferences(request):
""" View to display the settings of the tickets in the preferences page"""
pref, created = Preferences.objects.get_or_create(id=1)
context = {'preferences':pref,'language':str(pref.LANGUES[pref.mail_language][1])}
return render_to_string('tickets/preferences.html', context=context, request=request, using=None)
def contact(request):
"""View to display a contact address on the contact page
used here to display a link to open a ticket"""
return render_to_string('tickets/contact.html')
def navbar_user(request):
"""View to display the ticket link in thet user's dropdown in the navbar"""
return render_to_string('tickets/navbar.html')
def navbar_logout(request):
"""View to display the ticket link to log out users"""
return render_to_string('tickets/navbar_logout.html')

View file

@ -119,6 +119,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
</div>
</div>
<div class="panel-group" id="accordion">
<div class="panel panel-default">
<div class="panel-heading clearfix profil" data-parent="#accordion" data-toggle="collapse"
@ -527,6 +528,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
</div>
</div>
</div>
{% for template in optionnal_templates_list %}
{{ template }}
{% endfor %}
</div>
{% endblock %}

View file

@ -47,13 +47,17 @@ from django.http import HttpResponse
from django.http import HttpResponseRedirect
from django.views.decorators.csrf import csrf_exempt
from django.utils.translation import ugettext as _
from django.template import loader
from rest_framework.renderers import JSONRenderer
from reversion import revisions as reversion
from cotisations.models import Facture, Paiement
from machines.models import Machine
from preferences.models import OptionalUser, GeneralOption, AssoOption
from importlib import import_module
from re2o.settings_local import OPTIONNAL_APPS_RE2O
from re2o.views import form
from re2o.utils import (
all_has_access,
@ -974,6 +978,10 @@ def profil(request, users, **_kwargs):
request.GET.get('order'),
SortTable.MACHINES_INDEX
)
optionnal_apps = [import_module(app) for app in OPTIONNAL_APPS_RE2O]
optionnal_templates_list = [app.views.profil(request,users) for app in optionnal_apps]
pagination_large_number = GeneralOption.get_cached_value(
'pagination_large_number'
)
@ -1016,6 +1024,7 @@ def profil(request, users, **_kwargs):
'users': users,
'machines_list': machines,
'nb_machines': nb_machines,
'optionnal_templates_list': optionnal_templates_list,
'facture_list': factures,
'ban_list': bans,
'white_list': whitelists,