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

style: 🎨 Apply black and isort

This commit is contained in:
Yoann Pétri 2021-02-10 11:06:09 +01:00 committed by klafyvel
parent 6736caf9f4
commit ec2b4afd41
141 changed files with 1687 additions and 1942 deletions

View file

@ -31,12 +31,10 @@ from rest_framework.authentication import TokenAuthentication
class ExpiringTokenAuthentication(TokenAuthentication):
"""Authenticate a user if the provided token is valid and not expired.
"""
"""Authenticate a user if the provided token is valid and not expired."""
def authenticate_credentials(self, key):
"""See base class. Add the verification the token is not expired.
"""
"""See base class. Add the verification the token is not expired."""
base = super(ExpiringTokenAuthentication, self)
user, token = base.authenticate_credentials(key)

View file

@ -22,8 +22,9 @@
"""Defines the permission classes used in the API.
"""
from rest_framework import permissions, exceptions
from django.http import Http404
from rest_framework import exceptions, permissions
from . import acl

View file

@ -24,7 +24,6 @@
from rest_framework import serializers
# The namespace used for the API. It must match the namespace used in the
# urlpatterns to include the API URLs.
API_NAMESPACE = "api"
@ -59,7 +58,3 @@ class NamespacedHMSerializer(serializers.HyperlinkedModelSerializer):
serializer_related_field = NamespacedHRField
serializer_url_field = NamespacedHIField

View file

@ -28,12 +28,13 @@ can also be register. That way a complete API root page presenting all URLs
can be generated automatically.
"""
from django.urls import path, include
from importlib import import_module
from django.conf import settings
from django.urls import include, path
from . import views
from .routers import AllViewsRouter
from django.conf import settings
app_name = "api"

View file

@ -29,9 +29,9 @@ the response (JSON or other), the CSRF exempting, ...
import datetime
from django.conf import settings
from django.db.models import Q
from django.contrib.auth.models import Group
from rest_framework import viewsets, generics, views
from django.db.models import Q
from rest_framework import generics, views, viewsets
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.response import Response
@ -41,7 +41,6 @@ from .pagination import PageSizedPagination
from .permissions import ACLPermission
class ObtainExpiringAuthToken(ObtainAuthToken):
"""Exposes a view to obtain a authentication token.

View file

@ -29,8 +29,8 @@ from __future__ import unicode_literals
from django.contrib import admin
from reversion.admin import VersionAdmin
from .models import Facture, Article, Banque, Paiement, Cotisation, Vente
from .models import CustomInvoice, CostEstimate
from .models import (Article, Banque, CostEstimate, Cotisation, CustomInvoice,
Facture, Paiement, Vente)
class FactureAdmin(VersionAdmin):

View file

@ -23,13 +23,13 @@ from rest_framework import serializers
import cotisations.models as cotisations
import preferences.models as preferences
from api.serializers import NamespacedHRField, NamespacedHIField, NamespacedHMSerializer
from api.serializers import (NamespacedHIField, NamespacedHMSerializer,
NamespacedHRField)
from users.api.serializers import UserSerializer
class FactureSerializer(NamespacedHMSerializer):
"""Serialize `cotisations.models.Facture` objects.
"""
"""Serialize `cotisations.models.Facture` objects."""
class Meta:
model = cotisations.Facture
@ -54,8 +54,7 @@ class BaseInvoiceSerializer(NamespacedHMSerializer):
class VenteSerializer(NamespacedHMSerializer):
"""Serialize `cotisations.models.Vente` objects.
"""
"""Serialize `cotisations.models.Vente` objects."""
class Meta:
model = cotisations.Vente
@ -74,17 +73,24 @@ class VenteSerializer(NamespacedHMSerializer):
class ArticleSerializer(NamespacedHMSerializer):
"""Serialize `cotisations.models.Article` objects.
"""
"""Serialize `cotisations.models.Article` objects."""
class Meta:
model = cotisations.Article
fields = ("name", "prix", "duration_membership", "duration_days_membership", "duration_connection", "duration_days_connection", "type_user", "api_url")
fields = (
"name",
"prix",
"duration_membership",
"duration_days_membership",
"duration_connection",
"duration_days_connection",
"type_user",
"api_url",
)
class BanqueSerializer(NamespacedHMSerializer):
"""Serialize `cotisations.models.Banque` objects.
"""
"""Serialize `cotisations.models.Banque` objects."""
class Meta:
model = cotisations.Banque
@ -92,8 +98,7 @@ class BanqueSerializer(NamespacedHMSerializer):
class PaiementSerializer(NamespacedHMSerializer):
"""Serialize `cotisations.models.Paiement` objects.
"""
"""Serialize `cotisations.models.Paiement` objects."""
class Meta:
model = cotisations.Paiement
@ -101,17 +106,23 @@ class PaiementSerializer(NamespacedHMSerializer):
class CotisationSerializer(NamespacedHMSerializer):
"""Serialize `cotisations.models.Cotisation` objects.
"""
"""Serialize `cotisations.models.Cotisation` objects."""
class Meta:
model = cotisations.Cotisation
fields = ("vente", "type_cotisation", "date_start_con", "date_end_con", "date_start_memb", "date_end_memb", "api_url")
fields = (
"vente",
"type_cotisation",
"date_start_con",
"date_end_con",
"date_start_memb",
"date_end_memb",
"api_url",
)
class ReminderUsersSerializer(UserSerializer):
"""Serialize the data about a mailing member.
"""
"""Serialize the data about a mailing member."""
class Meta(UserSerializer.Meta):
fields = ("get_full_name", "get_mail")

View file

@ -27,12 +27,11 @@ urls_viewset = [
(r"cotisations/article", views.ArticleViewSet, None),
(r"cotisations/banque", views.BanqueViewSet, None),
(r"cotisations/paiement", views.PaiementViewSet, None),
(r"cotisations/cotisation", views.CotisationViewSet, None)
(r"cotisations/cotisation", views.CotisationViewSet, None),
]
urls_view = [
(r"cotisations/reminder-get-users", views.ReminderView),
# Deprecated
(r"reminder/get-users", views.ReminderView),
]

View file

@ -19,71 +19,65 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from rest_framework import viewsets, generics
from rest_framework import generics, viewsets
from . import serializers
import cotisations.models as cotisations
import preferences.models as preferences
from . import serializers
class FactureViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Facture` objects.
"""
"""Exposes list and details of `cotisations.models.Facture` objects."""
queryset = cotisations.Facture.objects.all()
serializer_class = serializers.FactureSerializer
class FactureViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Facture` objects.
"""
"""Exposes list and details of `cotisations.models.Facture` objects."""
queryset = cotisations.BaseInvoice.objects.all()
serializer_class = serializers.BaseInvoiceSerializer
class VenteViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Vente` objects.
"""
"""Exposes list and details of `cotisations.models.Vente` objects."""
queryset = cotisations.Vente.objects.all()
serializer_class = serializers.VenteSerializer
class ArticleViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Article` objects.
"""
"""Exposes list and details of `cotisations.models.Article` objects."""
queryset = cotisations.Article.objects.all()
serializer_class = serializers.ArticleSerializer
class BanqueViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Banque` objects.
"""
"""Exposes list and details of `cotisations.models.Banque` objects."""
queryset = cotisations.Banque.objects.all()
serializer_class = serializers.BanqueSerializer
class PaiementViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Paiement` objects.
"""
"""Exposes list and details of `cotisations.models.Paiement` objects."""
queryset = cotisations.Paiement.objects.all()
serializer_class = serializers.PaiementSerializer
class CotisationViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Cotisation` objects.
"""
"""Exposes list and details of `cotisations.models.Cotisation` objects."""
queryset = cotisations.Cotisation.objects.all()
serializer_class = serializers.CotisationSerializer
class ReminderView(generics.ListAPIView):
"""Output for users to remind an end of their subscription.
"""
"""Output for users to remind an end of their subscription."""
queryset = preferences.Reminder.objects.all()
serializer_class = serializers.ReminderSerializer

View file

@ -37,25 +37,18 @@ of each of the method.
from __future__ import unicode_literals
from django import forms
from django.db.models import Q
from django.forms import ModelForm, Form
from django.core.validators import MinValueValidator
from django.utils.translation import ugettext_lazy as _
from django.db.models import Q
from django.forms import Form, ModelForm
from django.shortcuts import get_object_or_404
from django.utils.translation import ugettext_lazy as _
from re2o.field_permissions import FieldPermissionFormMixin
from re2o.mixins import FormRevMixin
from re2o.widgets import AutocompleteModelWidget
from .models import (
Article,
Paiement,
Facture,
Banque,
CustomInvoice,
Vente,
CostEstimate,
)
from .models import (Article, Banque, CostEstimate, CustomInvoice, Facture,
Paiement, Vente)
from .payment_methods import balance

View file

@ -32,29 +32,29 @@ each.
"""
from __future__ import unicode_literals
from dateutil.relativedelta import relativedelta
from dateutil.relativedelta import relativedelta
from django.contrib import messages
from django.core.validators import MinValueValidator
from django.db import models
from django.db.models import Q, Max
from django.db.models.signals import post_save, post_delete
from django.db.models import Max, Q
from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver
from django.forms import ValidationError
from django.core.validators import MinValueValidator
from django.shortcuts import redirect
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django.urls import reverse
from django.shortcuts import redirect
from django.contrib import messages
from preferences.models import CotisationsOption
import users.models
import users.signals
from cotisations.utils import (find_payment_method, send_mail_invoice,
send_mail_voucher)
from cotisations.validators import check_no_balance
from machines.models import regen
from preferences.models import CotisationsOption
from re2o.field_permissions import FieldPermissionModelMixin
from re2o.mixins import AclMixin, RevMixin
import users.signals
import users.models
from cotisations.utils import find_payment_method, send_mail_invoice, send_mail_voucher
from cotisations.validators import check_no_balance
class BaseInvoice(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
@ -360,7 +360,13 @@ def facture_post_save(**kwargs):
if facture.valid:
user = facture.user
user.set_active()
users.signals.synchronise.send(sender=users.models.User, instance=user, base=False, access_refresh=True, mac_refresh=False)
users.signals.synchronise.send(
sender=users.models.User,
instance=user,
base=False,
access_refresh=True,
mac_refresh=False,
)
@receiver(post_delete, sender=Facture)
@ -369,7 +375,13 @@ def facture_post_delete(**kwargs):
Synchronise the LDAP user after an invoice has been deleted.
"""
user = kwargs["instance"].user
users.signals.synchronise.send(sender=users.models.User, instance=user, base=False, access_refresh=True, mac_refresh=False)
users.signals.synchronise.send(
sender=users.models.User,
instance=user,
base=False,
access_refresh=True,
mac_refresh=False,
)
class CustomInvoice(BaseInvoice):
@ -481,9 +493,7 @@ class Vente(RevMixin, AclMixin, models.Model):
)
class Meta:
permissions = (
("change_all_vente", _("Can edit all the previous purchases")),
)
permissions = (("change_all_vente", _("Can edit all the previous purchases")),)
verbose_name = _("purchase")
verbose_name_plural = _("purchases")
@ -660,7 +670,13 @@ def vente_post_save(**kwargs):
purchase.cotisation.save()
user = purchase.facture.facture.user
user.set_active()
users.signals.synchronise.send(sender=users.models.User, instance=user, base=True, access_refresh=True, mac_refresh=False)
users.signals.synchronise.send(
sender=users.models.User,
instance=user,
base=True,
access_refresh=True,
mac_refresh=False,
)
# TODO : change vente to purchase
@ -676,7 +692,13 @@ def vente_post_delete(**kwargs):
return
if purchase.type_cotisation:
user = invoice.user
users.signals.synchronise.send(sender=users.models.User, instance=user, base=True, access_refresh=True, mac_refresh=False)
users.signals.synchronise.send(
sender=users.models.User,
instance=user,
base=True,
access_refresh=True,
mac_refresh=False,
)
class Article(RevMixin, AclMixin, models.Model):
@ -740,9 +762,7 @@ class Article(RevMixin, AclMixin, models.Model):
unique_together = ("name", "type_user")
class Meta:
permissions = (
("buy_every_article", _("Can buy every article")),
)
permissions = (("buy_every_article", _("Can buy every article")),)
verbose_name = "article"
verbose_name_plural = "articles"
@ -844,9 +864,7 @@ class Paiement(RevMixin, AclMixin, models.Model):
)
class Meta:
permissions = (
("use_every_payment", _("Can use every payment method")),
)
permissions = (("use_every_payment", _("Can use every payment method")),)
verbose_name = _("payment method")
verbose_name_plural = _("payment methods")

View file

@ -127,6 +127,6 @@ method to your model, where `form` is an instance of
"""
from . import comnpay, cheque, balance, note_kfet, free, urls
from . import balance, cheque, comnpay, free, note_kfet, urls
PAYMENT_METHODS = [comnpay, cheque, balance, note_kfet, free]

View file

@ -18,12 +18,11 @@
# 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.
from django.contrib import messages
from django.db import models
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from django.contrib import messages
from cotisations.models import Paiement
from cotisations.payment_methods.mixins import PaymentMethodMixin

View file

@ -20,8 +20,8 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from django import forms
from re2o.mixins import FormRevMixin
from cotisations.models import Facture as Invoice
from re2o.mixins import FormRevMixin
class InvoiceForm(FormRevMixin, forms.ModelForm):

View file

@ -19,6 +19,7 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from django.conf.urls import url
from . import views
urlpatterns = [url(r"^validate/(?P<invoice_pk>[0-9]+)$", views.cheque, name="validate")]

View file

@ -23,17 +23,17 @@
Here are defined some views dedicated to cheque payement.
"""
from django.urls import reverse
from django.shortcuts import redirect, render, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.translation import ugettext as _
from cotisations.models import Facture as Invoice
from cotisations.utils import find_payment_method
from .models import ChequePayment
from .forms import InvoiceForm
from .models import ChequePayment
@login_required

View file

@ -3,15 +3,15 @@ The module in charge of handling the negociation with Comnpay
for online payment
"""
import time
from random import randrange
import base64
import hashlib
import time
from collections import OrderedDict
from random import randrange
class Transaction:
""" The class representing a transaction with all the functions
"""The class representing a transaction with all the functions
used during the negociation
"""
@ -35,7 +35,7 @@ class Transaction:
self.idTransaction = ""
def buildSecretHTML(self, produit="Produit", montant="0.00", idTransaction=""):
""" Build an HTML hidden form with the different parameters for the
"""Build an HTML hidden form with the different parameters for the
transaction
"""
if idTransaction == "":

View file

@ -25,8 +25,8 @@ from django.utils.translation import ugettext_lazy as _
from cotisations.models import Paiement
from cotisations.payment_methods.mixins import PaymentMethodMixin
from re2o.aes_field import AESEncryptedField
from .comnpay import Transaction
@ -53,8 +53,7 @@ class ComnpayPayment(PaymentMethodMixin, models.Model):
minimum_payment = models.DecimalField(
verbose_name=_("minimum payment"),
help_text=_(
"The minimal amount of money you have to use when paying with"
" ComNpay."
"The minimal amount of money you have to use when paying with" " ComNpay."
),
max_digits=5,
decimal_places=2,
@ -107,8 +106,7 @@ class ComnpayPayment(PaymentMethodMixin, models.Model):
return render(request, "cotisations/payment.html", r)
def check_price(self, price, *args, **kwargs):
"""Checks that the price meets the requirement to be paid with ComNpay.
"""
"""Checks that the price meets the requirement to be paid with ComNpay."""
return (
(price >= self.minimum_payment),
_(

View file

@ -19,6 +19,7 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from django.conf.urls import url
from . import views
urlpatterns = [

View file

@ -25,16 +25,17 @@ Here are the views needed by comnpay
from collections import OrderedDict
from django.urls import reverse
from django.shortcuts import redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
from django.utils.datastructures import MultiValueDictKeyError
from django.utils.translation import ugettext as _
from django.http import HttpResponse, HttpResponseBadRequest
from django.views.decorators.csrf import csrf_exempt
from cotisations.models import Facture
from .comnpay import Transaction
from .models import ComnpayPayment
@ -55,7 +56,10 @@ def accept_payment(request, factureid):
)
# In case a cotisation was bought, inform the user, the
# cotisation time has been extended too
if any(purchase.test_membership_or_connection() for purchase in invoice.vente_set.all()):
if any(
purchase.test_membership_or_connection()
for purchase in invoice.vente_set.all()
):
messages.success(
request,
_(

View file

@ -21,9 +21,10 @@
from django import forms
from django.utils.translation import ugettext_lazy as _
from . import PAYMENT_METHODS
from cotisations.utils import find_payment_method
from . import PAYMENT_METHODS
def payment_method_factory(payment, *args, creation=True, **kwargs):
"""This function finds the right payment method form for a given payment.

View file

@ -18,10 +18,9 @@
# 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.
from django.contrib import messages
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.contrib import messages
from cotisations.models import Paiement
from cotisations.payment_methods.mixins import PaymentMethodMixin
@ -43,8 +42,7 @@ class FreePayment(PaymentMethodMixin, models.Model):
)
def end_payment(self, invoice, request):
"""Ends the payment normally.
"""
"""Ends the payment normally."""
return invoice.paiement.end_payment(invoice, request, use_payment_method=False)
def check_price(self, price, user, *args, **kwargs):

View file

@ -19,17 +19,15 @@
# 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.
from django.contrib import messages
from django.db import models
from django.shortcuts import render
from django.shortcuts import redirect, render
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from django.contrib import messages
from cotisations.models import Paiement
from cotisations.payment_methods.mixins import PaymentMethodMixin
from django.shortcuts import render, redirect
class NotePayment(PaymentMethodMixin, models.Model):
"""

View file

@ -5,8 +5,8 @@
""" Module pour dialoguer avec la NoteKfet2015 """
import socket
import json
import socket
import ssl
import traceback

View file

@ -19,6 +19,7 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from django.conf.urls import url
from . import views
urlpatterns = [

View file

@ -26,22 +26,23 @@ Here are the views needed by comnpay
from collections import OrderedDict
from django.urls import reverse
from django.shortcuts import redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
from django.utils.datastructures import MultiValueDictKeyError
from django.utils.translation import ugettext as _
from django.http import HttpResponse, HttpResponseBadRequest
from django.views.decorators.csrf import csrf_exempt
from cotisations.models import Facture
from cotisations.utils import find_payment_method
from .models import NotePayment
from re2o.views import form
from re2o.acl import can_create, can_edit
from .note import login, don
from re2o.views import form
from .forms import NoteCredentialForm
from .models import NotePayment
from .note import don, login
@login_required

View file

@ -19,10 +19,11 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from django.conf.urls import include, url
from . import comnpay, cheque, note_kfet
from . import cheque, comnpay, note_kfet
urlpatterns = [
url(r"^comnpay/", include((comnpay.urls, 'comnpay'), namespace="comnpay")),
url(r"^cheque/", include((cheque.urls, 'cheque'), namespace="cheque")),
url(r"^note_kfet/", include((note_kfet.urls, 'note_kfet'), namespace="note_kfet")),
url(r"^comnpay/", include((comnpay.urls, "comnpay"), namespace="comnpay")),
url(r"^cheque/", include((cheque.urls, "cheque"), namespace="cheque")),
url(r"^note_kfet/", include((note_kfet.urls, "note_kfet"), namespace="note_kfet")),
]

View file

@ -1,11 +1,12 @@
from django.test import TestCase
import datetime
from django.utils import timezone
from dateutil.relativedelta import relativedelta
from django.test import TestCase
from django.utils import timezone
from users.models import User
from .models import Vente, Facture, Cotisation, Paiement
from .models import Cotisation, Facture, Paiement, Vente
class VenteModelTests(TestCase):
@ -111,12 +112,21 @@ class VenteModelTests(TestCase):
duration_days_connection=1,
duration_membership=0,
duration_deys_membership=1,
prix=0
prix=0,
)
v.create_cotis(
date_start_con=timezone.make_aware(datetime.datetime(1998, 10, 16)),
date_start_memb=timezone.make_aware(datetime.datetime(1998, 10, 16)),
)
v.create_cotis(date_start_con=timezone.make_aware(datetime.datetime(1998, 10, 16)), date_start_memb=timezone.make_aware(datetime.datetime(1998, 10, 16)))
v.save()
self.assertEqual(v.cotisation.date_end_con, timezone.make_aware(datetime.datetime(1998, 10, 17)))
self.assertEqual(v.cotisation.date_end_memb, timezone.make_aware(datetime.datetime(1998, 10, 17)))
self.assertEqual(
v.cotisation.date_end_con,
timezone.make_aware(datetime.datetime(1998, 10, 17)),
)
self.assertEqual(
v.cotisation.date_end_memb,
timezone.make_aware(datetime.datetime(1998, 10, 17)),
)
def test_one_day_cotisation_membership_only(self):
"""
@ -207,12 +217,21 @@ class VenteModelTests(TestCase):
duration_days_connection=0,
duration_membership=0,
duration_days_membership=1,
prix=0
prix=0,
)
v.create_cotis(
date_start_con=timezone.make_aware(datetime.datetime(1998, 10, 16)),
date_start_memb=timezone.make_aware(datetime.datetime(1998, 10, 16)),
)
v.create_cotis(date_start_con=timezone.make_aware(datetime.datetime(1998, 10, 16)), date_start_memb=timezone.make_aware(datetime.datetime(1998, 10, 16)))
v.save()
self.assertEqual(v.cotisation.date_end_con, timezone.make_aware(datetime.datetime(1998, 10, 17)))
self.assertEqual(v.cotisation.date_end_memb, timezone.make_aware(datetime.datetime(1998, 10, 16)))
self.assertEqual(
v.cotisation.date_end_con,
timezone.make_aware(datetime.datetime(1998, 10, 17)),
)
self.assertEqual(
v.cotisation.date_end_memb,
timezone.make_aware(datetime.datetime(1998, 10, 16)),
)
def test_cotisation_membership_diff_connection(self):
"""
@ -252,9 +271,11 @@ class FactureModelTests(TestCase):
def setUp(self):
self.user = User.objects.create(pseudo="testUserPlop", email="test@example.org")
self.paiement = Paiement.objects.create(moyen="test payment")
def tearDown(self):
self.user.delete()
self.paiement.delete()
def test_cotisations_prolongation(self):
"""When user already have one valid cotisation, the new one should be
added at the end of the existing one."""
@ -300,4 +321,3 @@ class FactureModelTests(TestCase):
raise e
invoice1.delete()
invoice2.delete()

View file

@ -1,13 +1,14 @@
import datetime
from dateutil.relativedelta import relativedelta
from django.contrib.auth.models import Permission
from django.test import TestCase
from django.urls import reverse
from django.contrib.auth.models import Permission
import datetime
from dateutil.relativedelta import relativedelta
from django.utils import timezone
from users.models import Adherent
from .models import Vente, Facture, Cotisation, Paiement, Article
from .models import Article, Cotisation, Facture, Paiement, Vente
class NewFactureTests(TestCase):

View file

@ -26,20 +26,19 @@ Used to generated PDF invoice.
"""
import tempfile
from subprocess import Popen, PIPE
import os
import tempfile
from datetime import datetime
from subprocess import PIPE, Popen
from django.db import models
from django.template.loader import get_template
from django.http import HttpResponse
from django.conf import settings
from django.db import models
from django.http import HttpResponse
from django.template.loader import get_template
from django.utils.text import slugify
from re2o.mixins import AclMixin, RevMixin
from preferences.models import CotisationsOption
from re2o.mixins import AclMixin, RevMixin
TEMP_PREFIX = getattr(settings, "TEX_TEMP_PREFIX", "render_tex-")
CACHE_PREFIX = getattr(settings, "TEX_CACHE_PREFIX", "render-tex")

View file

@ -27,23 +27,18 @@ from __future__ import unicode_literals
from django.urls import path
from . import views, views_autocomplete
from . import payment_methods
from . import payment_methods, views, views_autocomplete
app_name ="cotisations"
app_name = "cotisations"
urlpatterns = [
path("new_facture/<int:userid>", views.new_facture, name="new-facture"),
path(
"edit_facture/<int:factureid>", views.edit_facture, name="edit-facture"
),
path("edit_facture/<int:factureid>", views.edit_facture, name="edit-facture"),
path("del_facture/<int:factureid>", views.del_facture, name="del-facture"),
path("facture_pdf/<int:factureid>", views.facture_pdf, name="facture-pdf"),
path("voucher_pdf/<int:factureid>", views.voucher_pdf, name="voucher-pdf"),
path("new_cost_estimate", views.new_cost_estimate, name="new-cost-estimate"),
path(
"index_cost_estimate", views.index_cost_estimate, name="index-cost-estimate"
),
path("index_cost_estimate", views.index_cost_estimate, name="index-cost-estimate"),
path(
"cost_estimate_pdf/<int:costestimateid>",
views.cost_estimate_pdf,
@ -87,9 +82,7 @@ urlpatterns = [
),
path("credit_solde/<int:userid>", views.credit_solde, name="credit-solde"),
path("add_article", views.add_article, name="add-article"),
path(
"edit_article/<int:articleid>", views.edit_article, name="edit-article"
),
path("edit_article/<int:articleid>", views.edit_article, name="edit-article"),
path("del_article", views.del_article, name="del-article"),
path("add_paiement", views.add_paiement, name="add-paiement"),
path(
@ -107,5 +100,9 @@ urlpatterns = [
path("control", views.control, name="control"),
path("", views.index, name="index"),
### Autocomplete Views
path('banque-autocomplete', views_autocomplete.BanqueAutocomplete.as_view(), name='banque-autocomplete',),
path(
"banque-autocomplete",
views_autocomplete.BanqueAutocomplete.as_view(),
name="banque-autocomplete",
),
] + payment_methods.urls.urlpatterns

View file

@ -21,14 +21,16 @@
import os
from django.template.loader import get_template
from django.core.mail import EmailMessage
from django.template.loader import get_template
from preferences.models import (AssoOption, CotisationsOption, GeneralOption,
Mandate)
from re2o import settings
from re2o.mail_utils import send_mail_object
from re2o.settings import LOGO_PATH
from .tex import create_pdf
from preferences.models import AssoOption, GeneralOption, CotisationsOption, Mandate
from re2o.settings import LOGO_PATH
from re2o import settings
def find_payment_method(payment):
@ -74,7 +76,9 @@ def send_mail_invoice(invoice, request=None):
"tpl_path": os.path.join(settings.BASE_DIR, LOGO_PATH),
}
template = CotisationsOption.get_cached_value("invoice_template").template.name.split("/")[-1]
template = CotisationsOption.get_cached_value(
"invoice_template"
).template.name.split("/")[-1]
pdf = create_pdf(template, ctx)
template = get_template("cotisations/email_invoice")
@ -106,7 +110,9 @@ def send_mail_voucher(invoice, request=None):
"email": invoice.user.email,
"phone": invoice.user.telephone,
"date_end": invoice.get_subscription().latest("date_end_memb").date_end_memb,
"date_begin": invoice.get_subscription().earliest("date_start_memb").date_start_memb,
"date_begin": invoice.get_subscription()
.earliest("date_start_memb")
.date_start_memb,
}
templatename = CotisationsOption.get_cached_value(
"voucher_template"

View file

@ -29,62 +29,38 @@ The different views used in the Cotisations module
"""
from __future__ import unicode_literals
import os
from django.urls import reverse
from django.shortcuts import render, redirect, get_object_or_404
from django.template.loader import render_to_string
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.db.models import ProtectedError
from django.db.models import Q
from django.forms import modelformset_factory, formset_factory
from django.contrib.auth.decorators import login_required
from django.db.models import ProtectedError, Q
from django.forms import formset_factory, modelformset_factory
from django.shortcuts import get_object_or_404, redirect, render
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import ugettext as _
# Import des models, forms et fonctions re2o
from reversion import revisions as reversion
from users.models import User
from re2o.settings import LOGO_PATH
from re2o import settings
from re2o.views import form
from re2o.base import SortTable, re2o_paginator
from re2o.acl import (
can_create,
can_edit,
can_delete,
can_view,
can_view_all,
can_delete_set,
can_change,
)
from preferences.models import AssoOption, GeneralOption, Mandate
from .models import (
Facture,
Article,
Vente,
Paiement,
Banque,
CustomInvoice,
BaseInvoice,
CostEstimate,
)
from .forms import (
FactureForm,
ArticleForm,
DelArticleForm,
PaiementForm,
DelPaiementForm,
BanqueForm,
DelBanqueForm,
SelectArticleForm,
RechargeForm,
CustomInvoiceForm,
DiscountForm,
CostEstimateForm,
)
from .tex import render_invoice, render_voucher, escape_chars
from re2o import settings
from re2o.acl import (can_change, can_create, can_delete, can_delete_set,
can_edit, can_view, can_view_all)
from re2o.base import SortTable, re2o_paginator
from re2o.settings import LOGO_PATH
from re2o.views import form
from users.models import User
from .forms import (ArticleForm, BanqueForm, CostEstimateForm,
CustomInvoiceForm, DelArticleForm, DelBanqueForm,
DelPaiementForm, DiscountForm, FactureForm, PaiementForm,
RechargeForm, SelectArticleForm)
from .models import (Article, Banque, BaseInvoice, CostEstimate, CustomInvoice,
Facture, Paiement, Vente)
from .payment_methods.forms import payment_method_factory
from .tex import escape_chars, render_invoice, render_voucher
from .utils import find_payment_method
@ -1057,12 +1033,15 @@ def voucher_pdf(request, invoice, **_kwargs):
"lastname": invoice.user.surname,
"email": invoice.user.email,
"phone": invoice.user.telephone,
"date_end": invoice.get_subscription().latest("date_end_memb").date_end_memb,
"date_end": invoice.get_subscription()
.latest("date_end_memb")
.date_end_memb,
"date_begin": invoice.date,
},
)
def aff_profil(request,user):
def aff_profil(request, user):
"""View used to display the cotisations on a user's profil."""
factures = Facture.objects.filter(user=user)
@ -1074,13 +1053,13 @@ def aff_profil(request,user):
)
pagination_large_number = GeneralOption.get_cached_value("pagination_large_number")
factures = re2o_paginator(request, factures,pagination_large_number)
factures = re2o_paginator(request, factures, pagination_large_number)
context = {
"users":user,
"users": user,
"facture_list": factures,
}
return render_to_string(
"cotisations/aff_profil.html",context=context,request=request,using=None
"cotisations/aff_profil.html", context=context, request=request, using=None
)

View file

@ -31,20 +31,13 @@ Here are defined the autocomplete class based view.
"""
from __future__ import unicode_literals
from django.db.models import Q, Value, CharField
from .models import (
Banque
)
from django.db.models import CharField, Q, Value
from re2o.acl import can_view_all
from re2o.views import AutocompleteViewMixin
from re2o.acl import (
can_view_all,
)
from .models import Banque
class BanqueAutocomplete(AutocompleteViewMixin):
obj_type = Banque

View file

@ -34,12 +34,12 @@ https://github.com/FreeRADIUS/freeradius-server/blob/master/src/modules/rlm_pyth
Inspired by Daniel Stan in Crans
"""
import logging
import os
import sys
import logging
import traceback
import radiusd # Magic module freeradius (radiusd.py is dummy)
import radiusd # Magic module freeradius (radiusd.py is dummy)
from django.core.wsgi import get_wsgi_application
from django.db.models import Q
@ -54,11 +54,10 @@ os.chdir(proj_path)
# This is so models get loaded.
application = get_wsgi_application()
from machines.models import Interface, IpList, Nas, Domain
from machines.models import Domain, Interface, IpList, Nas
from preferences.models import RadiusOption
from topologie.models import Port, Switch
from users.models import User
from preferences.models import RadiusOption
# Logging
@ -162,8 +161,7 @@ def authorize(data):
@radius_event
def post_auth(data):
""" Function called after the user is authenticated
"""
"""Function called after the user is authenticated"""
nas = data.get("NAS-IP-Address", data.get("NAS-Identifier", None))
nas_instance = find_nas_from_request(nas)

View file

@ -1,11 +1,8 @@
from django.contrib import admin
from .models import (
LdapUser,
LdapServiceUser,
LdapServiceUserGroup,
LdapUserGroup,
)
from .models import (LdapServiceUser, LdapServiceUserGroup, LdapUser,
LdapUserGroup)
class LdapUserAdmin(admin.ModelAdmin):
"""LdapUser Admin view. Can't change password, manage
@ -15,6 +12,7 @@ class LdapUserAdmin(admin.ModelAdmin):
Django ModelAdmin: Apply on django ModelAdmin
"""
list_display = ("name", "uidNumber", "login_shell")
exclude = ("user_password", "sambat_nt_password")
search_fields = ("name",)

View file

@ -2,4 +2,4 @@ from django.apps import AppConfig
class LdapSyncConfig(AppConfig):
name = 'ldap_sync'
name = "ldap_sync"

View file

@ -18,11 +18,12 @@
import subprocess
from base64 import decodebytes
from django.core.management.base import BaseCommand, CommandError
from django.conf import settings
from django.core.management.base import BaseCommand, CommandError
from users.models import User, ListRight
from ldap_sync.models import synchronise_user, synchronise_serviceuser, synchronise_usergroup
from ldap_sync.models import (synchronise_serviceuser, synchronise_user,
synchronise_usergroup)
from users.models import ListRight, User
def split_lines(lines):

View file

@ -19,8 +19,8 @@
#
from django.core.management.base import BaseCommand, CommandError
from users.models import User
from ldap_sync.models import synchronise_user
from users.models import User
class Command(BaseCommand):

View file

@ -1,18 +1,16 @@
import sys
from django.db import models
from django.conf import settings
from django.dispatch import receiver
from django.contrib.auth.models import Group
import ldapdb.models
import ldapdb.models.fields
import users.signals
import users.models
from django.conf import settings
from django.contrib.auth.models import Group
from django.db import models
from django.dispatch import receiver
import machines.models
import users.models
import users.signals
class LdapUser(ldapdb.models.Model):
"""A class representing a LdapUser in LDAP, its LDAP conterpart.
@ -110,12 +108,12 @@ def synchronise_user(sender, **kwargs):
* mac_refresh : Default `True`. When True, synchronise the list of mac addresses.
* group_refresh: Default `False`. When `True` synchronise the groups of the instance.
"""
base=kwargs.get('base', True)
access_refresh=kwargs.get('access_refresh', True)
mac_refresh=kwargs.get('mac_refresh', True )
group_refresh=kwargs.get('group_refresh', False)
base = kwargs.get("base", True)
access_refresh = kwargs.get("access_refresh", True)
mac_refresh = kwargs.get("mac_refresh", True)
group_refresh = kwargs.get("group_refresh", False)
user=kwargs["instance"]
user = kwargs["instance"]
if sys.version_info[0] >= 3 and (
user.state == user.STATE_ACTIVE
@ -136,9 +134,7 @@ def synchronise_user(sender, **kwargs):
user_ldap.dialupAccess = str(user.has_access())
user_ldap.home_directory = user.home_directory
user_ldap.mail = user.get_mail
user_ldap.given_name = (
user.surname.lower() + "_" + user.name.lower()[:3]
)
user_ldap.given_name = user.surname.lower() + "_" + user.name.lower()[:3]
user_ldap.gid = settings.LDAP["user_gid"]
if "{SSHA}" in user.password or "{SMD5}" in user.password:
# We remove the extra $ added at import from ldap
@ -169,9 +165,12 @@ def synchronise_user(sender, **kwargs):
# be part of the updated group (case of group removal)
for group in Group.objects.all():
if hasattr(group, "listright"):
synchronise_usergroup(users.models.ListRight, instance=group.listright)
synchronise_usergroup(
users.models.ListRight, instance=group.listright
)
user_ldap.save()
@receiver(users.signals.remove, sender=users.models.User)
def remove_user(sender, **kwargs):
user = kwargs["instance"]
@ -181,6 +180,7 @@ def remove_user(sender, **kwargs):
except LdapUser.DoesNotExist:
pass
@receiver(users.signals.remove_mass, sender=users.models.User)
def remove_users(sender, **kwargs):
queryset_users = kwargs["queryset"]
@ -217,6 +217,7 @@ class LdapUserGroup(ldapdb.models.Model):
def __str__(self):
return self.name
@receiver(users.signals.synchronise, sender=users.models.ListRight)
def synchronise_usergroup(sender, **kwargs):
group = kwargs["instance"]
@ -228,6 +229,7 @@ def synchronise_usergroup(sender, **kwargs):
group_ldap.members = [user.pseudo for user in group.user_set.all()]
group_ldap.save()
@receiver(users.signals.remove, sender=users.models.ListRight)
def remove_usergroup(sender, **kwargs):
group = kwargs["instance"]
@ -238,7 +240,6 @@ def remove_usergroup(sender, **kwargs):
pass
class LdapServiceUser(ldapdb.models.Model):
"""A class representing a ServiceUser in LDAP, its LDAP conterpart.
Synced from ServiceUser, with a copy of its attributes/fields into LDAP,
@ -296,6 +297,7 @@ def synchronise_serviceuser(sender, **kwargs):
user_ldap.save()
synchronise_serviceuser_group(user)
@receiver(users.signals.remove, sender=users.models.ServiceUser)
def remove_serviceuser(sender, **kwargs):
user = kwargs["instance"]
@ -331,4 +333,3 @@ class LdapServiceUserGroup(ldapdb.models.Model):
def __str__(self):
return self.name

View file

@ -1,5 +1,6 @@
from django.urls import path
from .import views
from . import views
app_name = "ldap_sync"

View file

@ -21,13 +21,11 @@
"""The forms used by the machine search view"""
import inspect
from django import forms
from django.forms import Form
from django.utils.translation import ugettext_lazy as _
from re2o.base import get_input_formats_help_text
from re2o.widgets import AutocompleteModelWidget
import inspect
# Import all models in which there are classes to be filtered on
import cotisations.models
@ -35,7 +33,8 @@ import machines.models
import preferences.models
import topologie.models
import users.models
from re2o.base import get_input_formats_help_text
from re2o.widgets import AutocompleteModelWidget
CHOICES_ACTION_TYPE = (
("users", _("Users")),

View file

@ -21,23 +21,17 @@
"""logs.models
The models definitions for the logs app
"""
from reversion.models import Version, Revision
from django.utils.translation import ugettext_lazy as _
from django.apps import apps
from django.contrib.auth.models import Group
from django.db.models import Q
from django.apps import apps
from netaddr import EUI
from django.utils.translation import ugettext_lazy as _
from macaddress.fields import default_dialect
from netaddr import EUI
from reversion.models import Revision, Version
from machines.models import IpList
from machines.models import Interface
from machines.models import Machine
from machines.models import MachineType
from users.models import User
from users.models import Adherent
from users.models import Club
from topologie.models import Room
from topologie.models import Port
from machines.models import Interface, IpList, Machine, MachineType
from topologie.models import Port, Room
from users.models import Adherent, Club, User
from .forms import classes_for_action_type
@ -53,13 +47,12 @@ def make_version_filter(key, value):
# The lookup is done in a json string, so it has to be formated
# based on the value's type (to add " or not)
if type(value) is str:
formatted_value = "\"{}\"".format(value)
formatted_value = '"{}"'.format(value)
else:
formatted_value = str(value)
return (
Q(serialized_data__contains='\"{}\": {},'.format(key, formatted_value))
| Q(serialized_data__contains='\"{}\": {}}}'.format(key, formatted_value))
return Q(serialized_data__contains='"{}": {},'.format(key, formatted_value)) | Q(
serialized_data__contains='"{}": {}}}'.format(key, formatted_value)
)
@ -67,6 +60,7 @@ def make_version_filter(key, value):
# Machine history search #
############################
class MachineHistorySearchEvent:
def __init__(self, user, machine, interface, start=None, end=None):
"""Initialise an instance of MachineHistorySearchEvent.
@ -113,7 +107,7 @@ class MachineHistorySearchEvent:
self.ipv4,
self.start_date,
self.end_date,
self.comment or "No comment"
self.comment or "No comment",
)
@ -300,6 +294,7 @@ class MachineHistorySearch:
# Generic history classes #
############################
class RelatedHistory:
def __init__(self, version):
"""Initialise an instance of RelatedHistory.
@ -317,10 +312,7 @@ class RelatedHistory:
self.name = "{}: {}".format(self.model_name.title(), self.name)
def __eq__(self, other):
return (
self.model_name == other.model_name
and self.object_id == other.object_id
)
return self.model_name == other.model_name and self.object_id == other.object_id
def __hash__(self):
return hash((self.model_name, self.object_id))
@ -382,15 +374,11 @@ class HistoryEvent:
# Take into account keys that may exist in only one dict
if field in self.previous_version.field_dict:
old_value = self._repr(
field,
self.previous_version.field_dict[field]
field, self.previous_version.field_dict[field]
)
if field in self.version.field_dict:
new_value = self._repr(
field,
self.version.field_dict[field]
)
new_value = self._repr(field, self.version.field_dict[field])
edits.append((field, old_value, new_value))
@ -487,6 +475,7 @@ class History:
# Revision history #
############################
class VersionAction(HistoryEvent):
def __init__(self, version):
self.version = version
@ -533,15 +522,14 @@ class VersionAction(HistoryEvent):
"""
model = self.object_type()
try:
query = (
make_version_filter("pk", self.object_id())
& Q(
query = make_version_filter("pk", self.object_id()) & Q(
revision__date_created__lt=self.version.revision.date_created
)
)
return (Version.objects.get_for_model(model)
return (
Version.objects.get_for_model(model)
.filter(query)
.order_by("-revision__date_created")[0])
.order_by("-revision__date_created")[0]
)
except Exception:
return None
@ -648,6 +636,7 @@ class ActionsSearch:
# Class-specific history #
############################
class UserHistoryEvent(HistoryEvent):
def _repr(self, name, value):
"""Get the appropriate representation of the given field.
@ -733,13 +722,15 @@ class UserHistoryEvent(HistoryEvent):
)
def __hash__(self):
return hash((frozenset(self.edited_fields), self.date, self.performed_by, self.comment))
return hash(
(frozenset(self.edited_fields), self.date, self.performed_by, self.comment)
)
def __repr__(self):
return "{} edited fields {} ({})".format(
self.performed_by,
self.edited_fields or "nothing",
self.comment or "No comment"
self.comment or "No comment",
)
@ -762,9 +753,8 @@ class UserHistory(History):
# Try to find an Adherent object
# If it exists, its id will be the same as the user's
adherents = (
Version.objects.get_for_model(Adherent)
.filter(make_version_filter("pk", user_id))
adherents = Version.objects.get_for_model(Adherent).filter(
make_version_filter("pk", user_id)
)
try:
obj = adherents[0]
@ -774,9 +764,8 @@ class UserHistory(History):
# Fallback on a Club
if obj is None:
clubs = (
Version.objects.get_for_model(Club)
.filter(make_version_filter("pk", user_id))
clubs = Version.objects.get_for_model(Club).filter(
make_version_filter("pk", user_id)
)
try:
@ -826,11 +815,7 @@ class UserHistory(History):
# Remove duplicates and sort
self.events = list(dict.fromkeys(self.events))
return sorted(
self.events,
key=lambda e: e.date,
reverse=True
)
return sorted(self.events, key=lambda e: e.date, reverse=True)
def _add_revision(self, version):
"""Add a new revision to the chronological order.
@ -843,7 +828,7 @@ class UserHistory(History):
diff = self._compute_diff(
version,
self._last_version,
ignoring=["last_login", "pwd_ntlm", "email_change_date"]
ignoring=["last_login", "pwd_ntlm", "email_change_date"],
)
# Ignore "empty" events like login
@ -973,7 +958,7 @@ HISTORY_CLASS_MAPPING = {
User: UserHistory,
Machine: MachineHistory,
Interface: InterfaceHistory,
"default": History
"default": History,
}

View file

@ -47,5 +47,9 @@ urlpatterns = [
path("stats_models", views.stats_models, name="stats-models"),
path("stats_users", views.stats_users, name="stats-users"),
path("stats_actions", views.stats_actions, name="stats-actions"),
path("stats_search_machine", views.stats_search_machine_history, name="stats-search-machine"),
path(
"stats_search_machine",
views.stats_search_machine_history,
name="stats-search-machine",
),
]

View file

@ -38,84 +38,38 @@ objects for per model, number of actions per user etc.
from __future__ import unicode_literals
from django.urls import reverse
from django.shortcuts import render, redirect
from django.apps import apps
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import Http404
from django.db.models import Count
from django.apps import apps
from django.http import Http404
from django.shortcuts import redirect, render
from django.urls import reverse
from django.utils.translation import ugettext as _
from reversion.models import ContentType, Revision, Version
from reversion.models import Revision
from reversion.models import Version, ContentType
from users.models import (
User,
ServiceUser,
School,
ListRight,
ListShell,
Ban,
Whitelist,
Adherent,
Club,
)
from cotisations.models import Facture, Vente, Article, Banque, Paiement, Cotisation
from machines.models import (
Machine,
MachineType,
IpType,
Extension,
Interface,
Domain,
IpList,
OuverturePortList,
Service,
Vlan,
Nas,
SOA,
Mx,
Ns,
)
from topologie.models import (
Switch,
Port,
Room,
Stack,
ModelSwitch,
ConstructorSwitch,
AccessPoint,
)
from cotisations.models import (Article, Banque, Cotisation, Facture, Paiement,
Vente)
from machines.models import (SOA, Domain, Extension, Interface, IpList, IpType,
Machine, MachineType, Mx, Nas, Ns,
OuverturePortList, Service, Vlan)
from preferences.models import GeneralOption
from re2o.acl import (acl_error_message, can_edit_history, can_view,
can_view_all, can_view_app)
from re2o.base import SortTable, re2o_paginator
from re2o.utils import (all_active_assigned_interfaces_count,
all_active_interfaces_count, all_adherent, all_baned,
all_has_access, all_whitelisted)
from re2o.views import form
from re2o.utils import (
all_whitelisted,
all_baned,
all_has_access,
all_adherent,
all_active_assigned_interfaces_count,
all_active_interfaces_count,
)
from re2o.base import re2o_paginator, SortTable
from re2o.acl import (
can_view_all,
can_view_app,
can_edit_history,
can_view,
acl_error_message,
)
from .models import (
ActionsSearch,
RevisionAction,
MachineHistorySearch,
get_history_class,
)
from .forms import ActionsSearchForm, MachineHistorySearchForm
from topologie.models import (AccessPoint, ConstructorSwitch, ModelSwitch,
Port, Room, Stack, Switch)
from users.models import (Adherent, Ban, Club, ListRight, ListShell, School,
ServiceUser, User, Whitelist)
from .acl import can_view as can_view_logs
from .forms import ActionsSearchForm, MachineHistorySearchForm
from .models import (ActionsSearch, MachineHistorySearch, RevisionAction,
get_history_class)
@login_required
@ -528,7 +482,11 @@ def stats_search_machine_history(request):
max_result = GeneralOption.get_cached_value("pagination_number")
events = re2o_paginator(request, events, max_result)
return render(request, "logs/machine_history.html", {"events": events},)
return render(
request,
"logs/machine_history.html",
{"events": events},
)
return render(
request, "logs/search_machine_history.html", {"history_form": history_form}
)

View file

@ -29,24 +29,10 @@ from __future__ import unicode_literals
from django.contrib import admin
from reversion.admin import VersionAdmin
from .models import (
Extension,
SOA,
Mx,
Ns,
Vlan,
Txt,
DName,
Srv,
SshFp,
Nas,
Service,
Role,
OuverturePort,
Ipv6List,
OuverturePortList,
)
from .models import IpType, Machine, MachineType, Domain, IpList, Interface
from .models import (SOA, DName, Domain, Extension, Interface, IpList, IpType,
Ipv6List, Machine, MachineType, Mx, Nas, Ns,
OuverturePort, OuverturePortList, Role, Service, Srv,
SshFp, Txt, Vlan)
class MachineAdmin(VersionAdmin):

View file

@ -22,12 +22,12 @@
from rest_framework import serializers
import machines.models as machines
from api.serializers import NamespacedHRField, NamespacedHIField, NamespacedHMSerializer
from api.serializers import (NamespacedHIField, NamespacedHMSerializer,
NamespacedHRField)
class MachineSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Machine` objects.
"""
"""Serialize `machines.models.Machine` objects."""
class Meta:
model = machines.Machine
@ -35,8 +35,7 @@ class MachineSerializer(NamespacedHMSerializer):
class MachineTypeSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.MachineType` objects.
"""
"""Serialize `machines.models.MachineType` objects."""
class Meta:
model = machines.MachineType
@ -44,8 +43,7 @@ class MachineTypeSerializer(NamespacedHMSerializer):
class IpTypeSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.IpType` objects.
"""
"""Serialize `machines.models.IpType` objects."""
class Meta:
model = machines.IpType
@ -63,8 +61,7 @@ class IpTypeSerializer(NamespacedHMSerializer):
class VlanSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Vlan` objects.
"""
"""Serialize `machines.models.Vlan` objects."""
class Meta:
model = machines.Vlan
@ -82,8 +79,7 @@ class VlanSerializer(NamespacedHMSerializer):
class NasSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Nas` objects.
"""
"""Serialize `machines.models.Nas` objects."""
class Meta:
model = machines.Nas
@ -98,8 +94,7 @@ class NasSerializer(NamespacedHMSerializer):
class SOASerializer(NamespacedHMSerializer):
"""Serialize `machines.models.SOA` objects.
"""
"""Serialize `machines.models.SOA` objects."""
class Meta:
model = machines.SOA
@ -107,8 +102,7 @@ class SOASerializer(NamespacedHMSerializer):
class ExtensionSerializer(NamespacedHMSerializer):
"""Serialize machines.models.Extension objects.
"""
"""Serialize machines.models.Extension objects."""
class Meta:
model = machines.Extension
@ -116,8 +110,7 @@ class ExtensionSerializer(NamespacedHMSerializer):
class MxSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Mx` objects.
"""
"""Serialize `machines.models.Mx` objects."""
class Meta:
model = machines.Mx
@ -125,8 +118,7 @@ class MxSerializer(NamespacedHMSerializer):
class DNameSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.DName` objects.
"""
"""Serialize `machines.models.DName` objects."""
class Meta:
model = machines.DName
@ -134,8 +126,7 @@ class DNameSerializer(NamespacedHMSerializer):
class NsSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Ns` objects.
"""
"""Serialize `machines.models.Ns` objects."""
class Meta:
model = machines.Ns
@ -143,8 +134,7 @@ class NsSerializer(NamespacedHMSerializer):
class TxtSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Txt` objects.
"""
"""Serialize `machines.models.Txt` objects."""
class Meta:
model = machines.Txt
@ -152,8 +142,7 @@ class TxtSerializer(NamespacedHMSerializer):
class SrvSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Srv` objects.
"""
"""Serialize `machines.models.Srv` objects."""
class Meta:
model = machines.Srv
@ -171,8 +160,7 @@ class SrvSerializer(NamespacedHMSerializer):
class SshFpSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.SSHFP` objects.
"""
"""Serialize `machines.models.SSHFP` objects."""
class Meta:
model = machines.SshFp
@ -180,8 +168,7 @@ class SshFpSerializer(NamespacedHMSerializer):
class InterfaceSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Interface` objects.
"""
"""Serialize `machines.models.Interface` objects."""
mac_address = serializers.CharField()
active = serializers.BooleanField(source="is_active")
@ -201,8 +188,7 @@ class InterfaceSerializer(NamespacedHMSerializer):
class Ipv6ListSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Ipv6List` objects.
"""
"""Serialize `machines.models.Ipv6List` objects."""
class Meta:
model = machines.Ipv6List
@ -210,8 +196,7 @@ class Ipv6ListSerializer(NamespacedHMSerializer):
class DomainSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Domain` objects.
"""
"""Serialize `machines.models.Domain` objects."""
class Meta:
model = machines.Domain
@ -219,8 +204,7 @@ class DomainSerializer(NamespacedHMSerializer):
class IpListSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.IpList` objects.
"""
"""Serialize `machines.models.IpList` objects."""
class Meta:
model = machines.IpList
@ -228,8 +212,7 @@ class IpListSerializer(NamespacedHMSerializer):
class ServiceSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Service` objects.
"""
"""Serialize `machines.models.Service` objects."""
class Meta:
model = machines.Service
@ -243,8 +226,7 @@ class ServiceSerializer(NamespacedHMSerializer):
class ServiceLinkSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Service_link` objects.
"""
"""Serialize `machines.models.Service_link` objects."""
class Meta:
model = machines.Service_link
@ -260,8 +242,7 @@ class ServiceLinkSerializer(NamespacedHMSerializer):
class OuverturePortListSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.OuverturePortList` objects.
"""
"""Serialize `machines.models.OuverturePortList` objects."""
tcp_ports_in = NamespacedHRField(
view_name="ouvertureport-detail", many=True, read_only=True
@ -289,8 +270,7 @@ class OuverturePortListSerializer(NamespacedHMSerializer):
class OuverturePortSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.OuverturePort` objects.
"""
"""Serialize `machines.models.OuverturePort` objects."""
class Meta:
model = machines.OuverturePort
@ -298,8 +278,7 @@ class OuverturePortSerializer(NamespacedHMSerializer):
class RoleSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.OuverturePort` objects.
"""
"""Serialize `machines.models.OuverturePort` objects."""
servers = InterfaceSerializer(read_only=True, many=True)
@ -309,8 +288,7 @@ class RoleSerializer(NamespacedHMSerializer):
class ServiceRegenSerializer(NamespacedHMSerializer):
"""Serialize the data about the services to regen.
"""
"""Serialize the data about the services to regen."""
hostname = serializers.CharField(source="server.domain.name", read_only=True)
service_name = serializers.CharField(source="service.service_type", read_only=True)
@ -517,8 +495,7 @@ class DNAMERecordSerializer(serializers.ModelSerializer):
class DNSZonesSerializer(serializers.ModelSerializer):
"""Serialize the data about DNS Zones.
"""
"""Serialize the data about DNS Zones."""
soa = SOARecordSerializer()
ns_records = NSRecordSerializer(many=True, source="ns_set")
@ -559,8 +536,7 @@ class DNSZonesSerializer(serializers.ModelSerializer):
class DNSReverseZonesSerializer(serializers.ModelSerializer):
"""Serialize the data about DNS Zones.
"""
"""Serialize the data about DNS Zones."""
soa = SOARecordSerializer(source="extension.soa")
extension = serializers.CharField(source="extension.name", read_only=True)

View file

@ -45,9 +45,8 @@ urls_viewset = [
(r"machines/ouvertureport", views.OuverturePortViewSet, None),
(r"machines/role", views.RoleViewSet, None),
(r"machines/services-regen", views.ServiceRegenViewSet, "serviceregen"),
# Deprecated
(r"services/regen", views.ServiceRegenViewSet, "serviceregen")
(r"services/regen", views.ServiceRegenViewSet, "serviceregen"),
]
urls_view = [
@ -56,7 +55,6 @@ urls_view = [
(r"machines/firewall-interface-ports", views.InterfacePortsOpenView),
(r"machines/dns-zones", views.DNSZonesView),
(r"machines/dns-reverse-zones", views.DNSReverseZonesView),
# Deprecated
(r"dhcp/hostmacip", views.HostMacIpView),
(r"firewall/subnet-ports", views.SubnetPortsOpenView),

View file

@ -19,159 +19,142 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from rest_framework import viewsets, generics
from rest_framework import generics, viewsets
from . import serializers
import machines.models as machines
from re2o.utils import all_active_interfaces
from . import serializers
class MachineViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Machine` objects.
"""
"""Exposes list and details of `machines.models.Machine` objects."""
queryset = machines.Machine.objects.all()
serializer_class = serializers.MachineSerializer
class MachineTypeViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.MachineType` objects.
"""
"""Exposes list and details of `machines.models.MachineType` objects."""
queryset = machines.MachineType.objects.all()
serializer_class = serializers.MachineTypeSerializer
class IpTypeViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.IpType` objects.
"""
"""Exposes list and details of `machines.models.IpType` objects."""
queryset = machines.IpType.objects.all()
serializer_class = serializers.IpTypeSerializer
class VlanViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Vlan` objects.
"""
"""Exposes list and details of `machines.models.Vlan` objects."""
queryset = machines.Vlan.objects.all()
serializer_class = serializers.VlanSerializer
class NasViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Nas` objects.
"""
"""Exposes list and details of `machines.models.Nas` objects."""
queryset = machines.Nas.objects.all()
serializer_class = serializers.NasSerializer
class SOAViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.SOA` objects.
"""
"""Exposes list and details of `machines.models.SOA` objects."""
queryset = machines.SOA.objects.all()
serializer_class = serializers.SOASerializer
class ExtensionViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Extension` objects.
"""
"""Exposes list and details of `machines.models.Extension` objects."""
queryset = machines.Extension.objects.all()
serializer_class = serializers.ExtensionSerializer
class MxViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Mx` objects.
"""
"""Exposes list and details of `machines.models.Mx` objects."""
queryset = machines.Mx.objects.all()
serializer_class = serializers.MxSerializer
class NsViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Ns` objects.
"""
"""Exposes list and details of `machines.models.Ns` objects."""
queryset = machines.Ns.objects.all()
serializer_class = serializers.NsSerializer
class TxtViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Txt` objects.
"""
"""Exposes list and details of `machines.models.Txt` objects."""
queryset = machines.Txt.objects.all()
serializer_class = serializers.TxtSerializer
class DNameViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.DName` objects.
"""
"""Exposes list and details of `machines.models.DName` objects."""
queryset = machines.DName.objects.all()
serializer_class = serializers.DNameSerializer
class SrvViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Srv` objects.
"""
"""Exposes list and details of `machines.models.Srv` objects."""
queryset = machines.Srv.objects.all()
serializer_class = serializers.SrvSerializer
class SshFpViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.SshFp` objects.
"""
"""Exposes list and details of `machines.models.SshFp` objects."""
queryset = machines.SshFp.objects.all()
serializer_class = serializers.SshFpSerializer
class InterfaceViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Interface` objects.
"""
"""Exposes list and details of `machines.models.Interface` objects."""
queryset = machines.Interface.objects.all()
serializer_class = serializers.InterfaceSerializer
class Ipv6ListViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Ipv6List` objects.
"""
"""Exposes list and details of `machines.models.Ipv6List` objects."""
queryset = machines.Ipv6List.objects.all()
serializer_class = serializers.Ipv6ListSerializer
class DomainViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Domain` objects.
"""
"""Exposes list and details of `machines.models.Domain` objects."""
queryset = machines.Domain.objects.all()
serializer_class = serializers.DomainSerializer
class IpListViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.IpList` objects.
"""
"""Exposes list and details of `machines.models.IpList` objects."""
queryset = machines.IpList.objects.all()
serializer_class = serializers.IpListSerializer
class ServiceViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Service` objects.
"""
"""Exposes list and details of `machines.models.Service` objects."""
queryset = machines.Service.objects.all()
serializer_class = serializers.ServiceSerializer
class ServiceLinkViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Service_link` objects.
"""
"""Exposes list and details of `machines.models.Service_link` objects."""
queryset = machines.Service_link.objects.all()
serializer_class = serializers.ServiceLinkSerializer
@ -187,24 +170,21 @@ class OuverturePortListViewSet(viewsets.ReadOnlyModelViewSet):
class OuverturePortViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.OuverturePort` objects.
"""
"""Exposes list and details of `machines.models.OuverturePort` objects."""
queryset = machines.OuverturePort.objects.all()
serializer_class = serializers.OuverturePortSerializer
class RoleViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Machine` objects.
"""
"""Exposes list and details of `machines.models.Machine` objects."""
queryset = machines.Role.objects.all()
serializer_class = serializers.RoleSerializer
class ServiceRegenViewSet(viewsets.ModelViewSet):
"""Exposes list and details of the services to regen
"""
"""Exposes list and details of the services to regen"""
serializer_class = serializers.ServiceRegenSerializer
@ -238,6 +218,7 @@ class InterfacePortsOpenView(generics.ListAPIView):
queryset = machines.Interface.objects.filter(port_lists__isnull=False).distinct()
serializer_class = serializers.InterfacePortsOpenSerializer
class DNSZonesView(generics.ListAPIView):
"""Exposes the detailed information about each extension (hostnames,
IPs, DNS records, etc.) in order to build the DNS zone files.

View file

@ -36,37 +36,17 @@ Forms to create, edit and delete:
from __future__ import unicode_literals
from django import forms
from django.forms import ModelForm, Form
from django.forms import Form, ModelForm
from django.utils.translation import ugettext_lazy as _
from re2o.field_permissions import FieldPermissionFormMixin
from re2o.mixins import FormRevMixin
from re2o.widgets import (
AutocompleteModelWidget,
AutocompleteMultipleModelWidget,
)
from .models import (
Domain,
Machine,
Interface,
IpList,
MachineType,
Extension,
SOA,
Mx,
Txt,
DName,
Ns,
Role,
Service,
Vlan,
Srv,
SshFp,
Nas,
IpType,
OuverturePortList,
Ipv6List,
)
from re2o.widgets import (AutocompleteModelWidget,
AutocompleteMultipleModelWidget)
from .models import (SOA, DName, Domain, Extension, Interface, IpList, IpType,
Ipv6List, Machine, MachineType, Mx, Nas, Ns,
OuverturePortList, Role, Service, Srv, SshFp, Txt, Vlan)
class EditMachineForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
@ -248,7 +228,9 @@ class IpTypeForm(FormRevMixin, ModelForm):
fields = "__all__"
widgets = {
"vlan": AutocompleteModelWidget(url="/machines/vlan-autocomplete"),
"extension": AutocompleteModelWidget(url="/machines/extension-autocomplete"),
"extension": AutocompleteModelWidget(
url="/machines/extension-autocomplete"
),
"ouverture_ports": AutocompleteModelWidget(
url="/machines/ouvertureportlist-autocomplete"
),
@ -525,7 +507,9 @@ class SrvForm(FormRevMixin, ModelForm):
model = Srv
fields = "__all__"
widgets = {
"extension": AutocompleteModelWidget(url="/machines/extension-autocomplete"),
"extension": AutocompleteModelWidget(
url="/machines/extension-autocomplete"
),
"target": AutocompleteModelWidget(url="/machines/domain-autocomplete"),
}

View file

@ -35,26 +35,18 @@ from ipaddress import IPv6Address
from itertools import chain
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.db import models, transaction
from django.db.models import Q
from django.db.models.signals import post_save, post_delete
from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver
from django.forms import ValidationError
from django.utils import timezone
from django.db import transaction
from reversion import revisions as reversion
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from macaddress.fields import MACAddressField, default_dialect
from netaddr import (
mac_bare,
EUI,
NotRegisteredError,
IPSet,
IPRange,
IPNetwork,
IPAddress,
)
from netaddr import (EUI, IPAddress, IPNetwork, IPRange, IPSet,
NotRegisteredError, mac_bare)
from reversion import revisions as reversion
import preferences.models
import users.models
@ -79,9 +71,7 @@ class Machine(RevMixin, FieldPermissionModelMixin, AclMixin, models.Model):
active = models.BooleanField(default=True)
class Meta:
permissions = (
("change_machine_user", _("Can change the user of a machine")),
)
permissions = (("change_machine_user", _("Can change the user of a machine")),)
verbose_name = _("machine")
verbose_name_plural = _("machines")
@ -342,9 +332,7 @@ class MachineType(RevMixin, AclMixin, models.Model):
)
class Meta:
permissions = (
("use_all_machinetype", _("Can use all machine types")),
)
permissions = (("use_all_machinetype", _("Can use all machine types")),)
verbose_name = _("machine type")
verbose_name_plural = _("machine types")
@ -356,7 +344,9 @@ class MachineType(RevMixin, AclMixin, models.Model):
"""Update domains extension with the extension of interface_parent. Called after update of an ip_type or a machine_type object. Exceptions are handled in the views.
(Calling domain.clear() for all domains could take several minutes)
"""
Domain.objects.filter(interface_parent__machine_type=self).update(extension=self.ip_type.extension)
Domain.objects.filter(interface_parent__machine_type=self).update(
extension=self.ip_type.extension
)
@staticmethod
def can_use_all(user_request, *_args, **_kwargs):
@ -389,20 +379,13 @@ class MachineType(RevMixin, AclMixin, models.Model):
"""
can, _message, _group = cls.can_use_all(user_request)
if can:
return (
True,
None,
None,
cls.objects.all()
)
return (True, None, None, cls.objects.all())
else:
return (
True,
_("You don't have the right to use all machine types."),
("machines.use_all_machinetype",),
cls.objects.filter(
ip_type__in=IpType.objects.filter(need_infra=False)
),
cls.objects.filter(ip_type__in=IpType.objects.filter(need_infra=False)),
)
def __str__(self):
@ -455,12 +438,12 @@ class IpType(RevMixin, AclMixin, models.Model):
default=False, help_text=_("Enable reverse DNS for IPv6.")
)
vlan = models.ForeignKey("Vlan", on_delete=models.PROTECT, blank=True, null=True)
ouverture_ports = models.ForeignKey("OuverturePortList", blank=True, null=True, on_delete=models.PROTECT)
ouverture_ports = models.ForeignKey(
"OuverturePortList", blank=True, null=True, on_delete=models.PROTECT
)
class Meta:
permissions = (
("use_all_iptype", _("Can use all IP types")),
)
permissions = (("use_all_iptype", _("Can use all IP types")),)
verbose_name = _("IP type")
verbose_name_plural = _("IP types")
@ -897,9 +880,7 @@ class Extension(RevMixin, AclMixin, models.Model):
)
class Meta:
permissions = (
("use_all_extension", _("Can use all extensions")),
)
permissions = (("use_all_extension", _("Can use all extensions")),)
verbose_name = _("DNS extension")
verbose_name_plural = _("DNS extensions")
@ -987,12 +968,7 @@ class Extension(RevMixin, AclMixin, models.Model):
"""
can, _message, _group = cls.can_use_all(user_request)
if can:
return (
True,
None,
None,
cls.objects.all()
)
return (True, None, None, cls.objects.all())
else:
return (
True,
@ -1035,8 +1011,7 @@ class Mx(RevMixin, AclMixin, models.Model):
@cached_property
def dns_entry(self):
"""Get the complete DNS entry of the MX record, to put in zone files.
"""
"""Get the complete DNS entry of the MX record, to put in zone files."""
return "@ IN MX {prior} {name}".format(
prior=str(self.priority).ljust(3), name=str(self.name)
)
@ -1066,8 +1041,7 @@ class Ns(RevMixin, AclMixin, models.Model):
@cached_property
def dns_entry(self):
"""Get the complete DNS entry of the NS record, to put in zone files.
"""
"""Get the complete DNS entry of the NS record, to put in zone files."""
return "@ IN NS " + str(self.ns)
def __str__(self):
@ -1100,8 +1074,7 @@ class Txt(RevMixin, AclMixin, models.Model):
@cached_property
def dns_entry(self):
"""Get the complete DNS entry of the TXT record, to put in zone files.
"""
"""Get the complete DNS entry of the TXT record, to put in zone files."""
return str(self.field1).ljust(15) + " IN TXT " + str(self.field2)
@ -1129,8 +1102,7 @@ class DName(RevMixin, AclMixin, models.Model):
@cached_property
def dns_entry(self):
"""Get the complete DNS entry of the TXT record, to put in zone files.
"""
"""Get the complete DNS entry of the TXT record, to put in zone files."""
return str(self.alias).ljust(15) + " IN DNAME " + str(self.zone)
@ -1204,8 +1176,7 @@ class Srv(RevMixin, AclMixin, models.Model):
@cached_property
def dns_entry(self):
"""Get the complete DNS entry of the SRV record, to put in zone files.
"""
"""Get the complete DNS entry of the SRV record, to put in zone files."""
return (
str(self.service)
+ "._"
@ -1387,8 +1358,7 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
return vendor
def sync_ipv6_dhcpv6(self):
"""Assign an IPv6 address by DHCPv6, computed from the interface's ID.
"""
"""Assign an IPv6 address by DHCPv6, computed from the interface's ID."""
ipv6_dhcpv6 = self.gen_ipv6_dhcpv6
if not ipv6_dhcpv6:
return
@ -1414,8 +1384,7 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
ipv6_object.save()
def sync_ipv6(self):
"""Create and update the IPv6 addresses according to the IPv6 mode set.
"""
"""Create and update the IPv6 addresses according to the IPv6 mode set."""
if preferences.models.OptionalMachine.get_cached_value("ipv6_mode") == "SLAAC":
self.sync_ipv6_slaac()
elif (
@ -1581,9 +1550,11 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
_("You don't have the right to add a machine."),
("machines.add_interface",),
)
max_lambdauser_interfaces = preferences.models.OptionalMachine.get_cached_value(
max_lambdauser_interfaces = (
preferences.models.OptionalMachine.get_cached_value(
"max_lambdauser_interfaces"
)
)
if machine.user != user_request:
return (
False,
@ -1721,8 +1692,7 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
)
slaac_ip = models.BooleanField(default=False)
active = models.BooleanField(
default=True,
help_text=_("If false,the DNS will not provide this ip.")
default=True, help_text=_("If false,the DNS will not provide this ip.")
)
class Meta:
@ -1856,8 +1826,9 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
def check_and_replace_prefix(self, prefix=None):
"""Check if the IPv6 prefix is correct and update it if not."""
prefix_v6 = prefix or self.interface.machine_type.ip_type.prefix_v6.encode().decode(
"utf-8"
prefix_v6 = (
prefix
or self.interface.machine_type.ip_type.prefix_v6.encode().decode("utf-8")
)
if not prefix_v6:
return
@ -1930,7 +1901,11 @@ class Domain(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
)
extension = models.ForeignKey("Extension", on_delete=models.PROTECT)
cname = models.ForeignKey(
"self", null=True, blank=True, related_name="related_domain", on_delete=models.PROTECT
"self",
null=True,
blank=True,
related_name="related_domain",
on_delete=models.PROTECT,
)
ttl = models.PositiveIntegerField(
verbose_name=_("Time To Live (TTL)"),
@ -1940,9 +1915,7 @@ class Domain(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
class Meta:
unique_together = (("name", "extension"),)
permissions = (
("change_ttl", _("Can change the TTL of a domain object")),
)
permissions = (("change_ttl", _("Can change the TTL of a domain object")),)
verbose_name = _("domain")
verbose_name_plural = _("domains")
@ -2037,9 +2010,11 @@ class Domain(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
except Interface.DoesNotExist:
return False, _("Nonexistent interface."), None
if not user_request.has_perm("machines.add_domain"):
max_lambdauser_aliases = preferences.models.OptionalMachine.get_cached_value(
max_lambdauser_aliases = (
preferences.models.OptionalMachine.get_cached_value(
"max_lambdauser_aliases"
)
)
if interface.machine.user != user_request:
return (
False,
@ -2173,8 +2148,7 @@ class IpList(RevMixin, AclMixin, models.Model):
@cached_property
def need_infra(self):
"""Check if the 'infra' right is required to assign this IP address.
"""
"""Check if the 'infra' right is required to assign this IP address."""
return self.ip_type.need_infra
def clean(self):
@ -2206,20 +2180,13 @@ class IpList(RevMixin, AclMixin, models.Model):
"""
can, _message, _group = IpType.can_use_all(user_request)
if can:
return (
True,
None,
None,
cls.objects.all()
)
return (True, None, None, cls.objects.all())
else:
return (
True,
_("You don't have the right to use all machine types."),
("machines.use_all_machinetype",),
cls.objects.filter(
ip_type__in=IpType.objects.filter(need_infra=False)
),
cls.objects.filter(ip_type__in=IpType.objects.filter(need_infra=False)),
)
def __str__(self):
@ -2517,7 +2484,13 @@ class OuverturePort(RevMixin, AclMixin, models.Model):
def machine_post_save(**kwargs):
"""Synchronise LDAP and regen firewall/DHCP after a machine is edited."""
user = kwargs["instance"].user
users.signals.synchronise.send(sender=users.models.User, instance=user, base=False, access_refresh=False, mac_refresh=True)
users.signals.synchronise.send(
sender=users.models.User,
instance=user,
base=False,
access_refresh=False,
mac_refresh=True,
)
regen("dhcp")
regen("mac_ip_list")
@ -2527,7 +2500,13 @@ def machine_post_delete(**kwargs):
"""Synchronise LDAP and regen firewall/DHCP after a machine is deleted."""
machine = kwargs["instance"]
user = machine.user
users.signals.synchronise.send(sender=users.models.User, instance=user, base=False, access_refresh=False, mac_refresh=True)
users.signals.synchronise.send(
sender=users.models.User,
instance=user,
base=False,
access_refresh=False,
mac_refresh=True,
)
regen("dhcp")
regen("mac_ip_list")
@ -2540,7 +2519,13 @@ def interface_post_save(**kwargs):
interface = kwargs["instance"]
interface.sync_ipv6()
user = interface.machine.user
users.signals.synchronise.send(sender=users.models.User, instance=user, base=False, access_refresh=False, mac_refresh=True)
users.signals.synchronise.send(
sender=users.models.User,
instance=user,
base=False,
access_refresh=False,
mac_refresh=True,
)
# Regen services
regen("dhcp")
regen("mac_ip_list")
@ -2552,11 +2537,16 @@ def interface_post_save(**kwargs):
@receiver(post_delete, sender=Interface)
def interface_post_delete(**kwargs):
"""Synchronise LDAP and regen firewall/DHCP after an interface is deleted.
"""
"""Synchronise LDAP and regen firewall/DHCP after an interface is deleted."""
interface = kwargs["instance"]
user = interface.machine.user
users.signals.synchronise.send(sender=users.models.User, instance=user, base=False, access_refresh=False, mac_refresh=True)
users.signals.synchronise.send(
sender=users.models.User,
instance=user,
base=False,
access_refresh=False,
mac_refresh=True,
)
@receiver(post_save, sender=IpType)

View file

@ -28,8 +28,7 @@ from __future__ import unicode_literals
from django.urls import path
from . import views
from . import views_autocomplete
from . import views, views_autocomplete
app_name = "machines"
@ -96,9 +95,7 @@ urlpatterns = [
path("add_alias/<int:interfaceid>", views.add_alias, name="add-alias"),
path("edit_alias/<int:domainid>", views.edit_alias, name="edit-alias"),
path("del_alias/<int:interfaceid>", views.del_alias, name="del-alias"),
path(
"index_alias/<int:interfaceid>", views.index_alias, name="index-alias"
),
path("index_alias/<int:interfaceid>", views.index_alias, name="index-alias"),
path(
"new_ipv6list/<int:interfaceid>",
views.new_ipv6list,
@ -116,9 +113,7 @@ urlpatterns = [
),
path("index_ipv6/<int:interfaceid>", views.index_ipv6, name="index-ipv6"),
path("add_service", views.add_service, name="add-service"),
path(
"edit_service/<int:serviceid>", views.edit_service, name="edit-service"
),
path("edit_service/<int:serviceid>", views.edit_service, name="edit-service"),
path("del_service", views.del_service, name="del-service"),
path(
"regen_service/<int:serviceid>",
@ -157,13 +152,49 @@ urlpatterns = [
name="port-config",
),
### Autocomplete Views
path('vlan-autocomplete', views_autocomplete.VlanAutocomplete.as_view(), name='vlan-autocomplete',),
path('interface-autocomplete', views_autocomplete.InterfaceAutocomplete.as_view(), name='interface-autocomplete',),
path('machine-autocomplete', views_autocomplete.MachineAutocomplete.as_view(), name='machine-autocomplete',),
path('machinetype-autocomplete', views_autocomplete.MachineTypeAutocomplete.as_view(), name='machinetype-autocomplete',),
path('iptype-autocomplete', views_autocomplete.IpTypeAutocomplete.as_view(), name='iptype-autocomplete',),
path('extension-autocomplete', views_autocomplete.ExtensionAutocomplete.as_view(), name='extension-autocomplete',),
path('domain-autocomplete', views_autocomplete.DomainAutocomplete.as_view(), name='domain-autocomplete',),
path('ouvertureportlist-autocomplete', views_autocomplete.OuverturePortListAutocomplete.as_view(), name='ouvertureportlist-autocomplete',),
path('iplist-autocomplete', views_autocomplete.IpListAutocomplete.as_view(), name='iplist-autocomplete',),
path(
"vlan-autocomplete",
views_autocomplete.VlanAutocomplete.as_view(),
name="vlan-autocomplete",
),
path(
"interface-autocomplete",
views_autocomplete.InterfaceAutocomplete.as_view(),
name="interface-autocomplete",
),
path(
"machine-autocomplete",
views_autocomplete.MachineAutocomplete.as_view(),
name="machine-autocomplete",
),
path(
"machinetype-autocomplete",
views_autocomplete.MachineTypeAutocomplete.as_view(),
name="machinetype-autocomplete",
),
path(
"iptype-autocomplete",
views_autocomplete.IpTypeAutocomplete.as_view(),
name="iptype-autocomplete",
),
path(
"extension-autocomplete",
views_autocomplete.ExtensionAutocomplete.as_view(),
name="extension-autocomplete",
),
path(
"domain-autocomplete",
views_autocomplete.DomainAutocomplete.as_view(),
name="domain-autocomplete",
),
path(
"ouvertureportlist-autocomplete",
views_autocomplete.OuverturePortListAutocomplete.as_view(),
name="ouvertureportlist-autocomplete",
),
path(
"iplist-autocomplete",
views_autocomplete.IpListAutocomplete.as_view(),
name="iplist-autocomplete",
),
]

View file

@ -34,11 +34,11 @@ from __future__ import unicode_literals
from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required
from django.db.models import ProtectedError, F
from django.db import IntegrityError
from django.db.models import F, ProtectedError
from django.forms import modelformset_factory
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.shortcuts import redirect, render
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.translation import ugettext as _
@ -46,82 +46,28 @@ from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer
from preferences.models import GeneralOption
from re2o.acl import (
can_create,
can_edit,
can_view,
can_delete,
can_view_all,
can_delete_set,
)
from re2o.utils import all_active_assigned_interfaces, filter_active_interfaces
from re2o.acl import (can_create, can_delete, can_delete_set, can_edit,
can_view, can_view_all)
from re2o.base import SortTable, re2o_paginator
from re2o.utils import all_active_assigned_interfaces, filter_active_interfaces
from re2o.views import form
from users.models import User
from .forms import (
NewMachineForm,
EditMachineForm,
EditInterfaceForm,
AddInterfaceForm,
MachineTypeForm,
DelMachineTypeForm,
ExtensionForm,
DelExtensionForm,
EditIpTypeForm,
IpTypeForm,
DelIpTypeForm,
DomainForm,
AliasForm,
DelAliasForm,
SOAForm,
DelSOAForm,
NsForm,
DelNsForm,
TxtForm,
DelTxtForm,
DNameForm,
DelDNameForm,
MxForm,
DelMxForm,
VlanForm,
DelVlanForm,
RoleForm,
DelRoleForm,
ServiceForm,
DelServiceForm,
SshFpForm,
NasForm,
DelNasForm,
SrvForm,
DelSrvForm,
Ipv6ListForm,
EditOuverturePortListForm,
EditOuverturePortConfigForm,
)
from .models import (
IpType,
Machine,
Interface,
MachineType,
Extension,
SOA,
Mx,
Ns,
Domain,
Role,
Service,
Service_link,
regen,
Vlan,
Nas,
Txt,
DName,
Srv,
SshFp,
OuverturePortList,
OuverturePort,
Ipv6List,
)
from .forms import (AddInterfaceForm, AliasForm, DelAliasForm, DelDNameForm,
DelExtensionForm, DelIpTypeForm, DelMachineTypeForm,
DelMxForm, DelNasForm, DelNsForm, DelRoleForm,
DelServiceForm, DelSOAForm, DelSrvForm, DelTxtForm,
DelVlanForm, DNameForm, DomainForm, EditInterfaceForm,
EditIpTypeForm, EditMachineForm,
EditOuverturePortConfigForm, EditOuverturePortListForm,
ExtensionForm, IpTypeForm, Ipv6ListForm, MachineTypeForm,
MxForm, NasForm, NewMachineForm, NsForm, RoleForm,
ServiceForm, SOAForm, SrvForm, SshFpForm, TxtForm,
VlanForm)
from .models import (SOA, DName, Domain, Extension, Interface, IpType,
Ipv6List, Machine, MachineType, Mx, Nas, Ns,
OuverturePort, OuverturePortList, Role, Service,
Service_link, Srv, SshFp, Txt, Vlan, regen)
@login_required
@ -135,7 +81,9 @@ def new_machine(request, user, **_kwargs):
"""
machine = NewMachineForm(request.POST or None, user=request.user)
interface = AddInterfaceForm(request.POST or None, user=request.user)
domain = DomainForm(request.POST or None, user=user, initial={'name': user.get_next_domain_name()})
domain = DomainForm(
request.POST or None, user=user, initial={"name": user.get_next_domain_name()}
)
if machine.is_valid() and interface.is_valid():
new_machine_obj = machine.save(commit=False)
new_machine_obj.user = user
@ -229,7 +177,11 @@ def new_interface(request, machine, **_kwargs):
machine.
"""
interface_form = AddInterfaceForm(request.POST or None, user=request.user)
domain_form = DomainForm(request.POST or None, user=request.user, initial={'name': machine.user.get_next_domain_name()})
domain_form = DomainForm(
request.POST or None,
user=request.user,
initial={"name": machine.user.get_next_domain_name()},
)
if interface_form.is_valid():
new_interface_obj = interface_form.save(commit=False)
domain_form.instance.interface_parent = new_interface_obj
@ -268,7 +220,9 @@ def del_interface(request, interface, **_kwargs):
reverse("users:profil", kwargs={"userid": str(request.user.id)})
)
return form(
{"objet": interface, "objet_name": _("interface")}, "machines/delete.html", request
{"objet": interface, "objet_name": _("interface")},
"machines/delete.html",
request,
)
@ -328,7 +282,9 @@ def del_ipv6list(request, ipv6list, **_kwargs):
reverse("machines:index-ipv6", kwargs={"interfaceid": str(interfaceid)})
)
return form(
{"objet": ipv6list, "objet_name": _("IPv6 addresses list")}, "machines/delete.html", request
{"objet": ipv6list, "objet_name": _("IPv6 addresses list")},
"machines/delete.html",
request,
)
@ -384,7 +340,9 @@ def del_sshfp(request, sshfp, **_kwargs):
reverse("machines:index-sshfp", kwargs={"machineid": str(machineid)})
)
return form(
{"objet": sshfp, "objet_name": _("SSHFP record")}, "machines/delete.html", request
{"objet": sshfp, "objet_name": _("SSHFP record")},
"machines/delete.html",
request,
)
@ -421,7 +379,9 @@ def edit_iptype(request, iptype_instance, **_kwargs):
iptype.save()
messages.success(request, _("The IP type was edited."))
except IntegrityError as e:
messages.success(request, _("This IP type change would create duplicated domains"))
messages.success(
request, _("This IP type change would create duplicated domains")
)
return redirect(reverse("machines:index-iptype"))
return form(
{"iptypeform": iptype, "action_name": _("Edit")},
@ -490,7 +450,10 @@ def edit_machinetype(request, machinetype_instance, **_kwargs):
machinetype.save()
messages.success(request, _("The machine type was edited."))
except IntegrityError as e:
messages.error(request, _("This machine type change would create duplicated domains"))
messages.error(
request,
_("This machine type change would create duplicated domains"),
)
return redirect(reverse("machines:index-machinetype"))
return form(
{"machinetypeform": machinetype, "action_name": _("Edit")},
@ -580,10 +543,16 @@ def del_extension(request, instances):
_(
"The extension %s is assigned to following %s : %s"
", you can't delete it."
) % (
)
% (
extension_del,
str(e.protected_objects.model._meta.verbose_name_plural),
",".join(map(lambda x: str(x['name']), e.protected_objects.values('name').iterator()))
",".join(
map(
lambda x: str(x["name"]),
e.protected_objects.values("name").iterator(),
)
),
)
),
)
@ -1221,6 +1190,7 @@ def index(request):
machines_list = re2o_paginator(request, machines_list, pagination_large_number)
return render(request, "machines/index.html", {"machines_list": machines_list})
# Canonic view for displaying machines in users's profil
def aff_profil(request, user):
"""View used to display the machines on a user's profile."""
@ -1243,19 +1213,16 @@ def aff_profil(request, user):
machines = re2o_paginator(request, machines, pagination_large_number)
context = {
"users":user,
"users": user,
"machines_list": machines,
"nb_machines":nb_machines,
"nb_machines": nb_machines,
}
return render_to_string(
"machines/aff_profil.html",context=context,request=request,using=None
"machines/aff_profil.html", context=context, request=request, using=None
)
@login_required
@can_view_all(IpType)
def index_iptype(request):
@ -1531,4 +1498,3 @@ def configure_ports(request, interface_instance, **_kwargs):
"machines/machine.html",
request,
)

View file

@ -31,23 +31,14 @@ Here are defined the autocomplete class based view.
"""
from __future__ import unicode_literals
from django.db.models import Q, Value, CharField
from django.db.models import CharField, Q, Value
from django.db.models.functions import Concat
from .models import (
Interface,
Machine,
Vlan,
MachineType,
IpType,
Extension,
Domain,
OuverturePortList,
IpList,
)
from re2o.views import AutocompleteViewMixin
from .models import (Domain, Extension, Interface, IpList, IpType, Machine,
MachineType, OuverturePortList, Vlan)
class VlanAutocomplete(AutocompleteViewMixin):
obj_type = Vlan

View file

@ -27,11 +27,11 @@ Select a dorm
from django import forms
from django.forms import ModelForm, Form
from re2o.field_permissions import FieldPermissionFormMixin
from re2o.mixins import FormRevMixin
from django.forms import Form, ModelForm
from django.utils.translation import ugettext_lazy as _
from re2o.field_permissions import FieldPermissionFormMixin
from re2o.mixins import FormRevMixin
from topologie.models import Dormitory
from .preferences.models import MultiopOption
@ -49,4 +49,6 @@ class DormitoryForm(FormRevMixin, Form):
def __init__(self, *args, **kwargs):
super(DormitoryForm, self).__init__(*args, **kwargs)
self.fields["dormitory"].queryset = MultiopOption.get_cached_value("enabled_dorm").all()
self.fields["dormitory"].queryset = MultiopOption.get_cached_value(
"enabled_dorm"
).all()

View file

@ -25,21 +25,18 @@ Multi_op model
from __future__ import absolute_import
from django.core.mail import EmailMessage
from django.db import models
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.template import loader
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from reversion.models import Version
from re2o.mixins import AclMixin
from re2o.mail_utils import send_mail_object
from django.core.mail import EmailMessage
from preferences.models import GeneralOption
import users.models
from preferences.models import GeneralOption
from re2o.mail_utils import send_mail_object
from re2o.mixins import AclMixin
from .preferences.models import MultiopOption

View file

@ -27,8 +27,9 @@ each.
from django import forms
from django.forms import ModelForm, Form
from django.forms import Form, ModelForm
from django.utils.translation import ugettext_lazy as _
from re2o.widgets import AutocompleteMultipleModelWidget
from .models import MultiopOption

View file

@ -27,8 +27,8 @@ with multiple operators.
from django.db import models
from django.utils.translation import ugettext_lazy as _
from re2o.mixins import AclMixin, RevMixin
from preferences.models import PreferencesModel
from re2o.mixins import AclMixin, RevMixin
class MultiopOption(AclMixin, PreferencesModel):

View file

@ -26,20 +26,16 @@
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
from django.shortcuts import redirect, render
from django.template.loader import render_to_string
from django.utils.translation import ugettext as _
from django.urls import reverse
from re2o.base import re2o_paginator
from re2o.acl import can_view, can_view_all, can_edit, can_create
from django.utils.translation import ugettext as _
from preferences.views import edit_options_template_function
from re2o.acl import can_create, can_edit, can_view, can_view_all
from re2o.base import re2o_paginator
from . import forms
from . import models
from . import forms, models
def aff_preferences(request):

View file

@ -27,28 +27,24 @@
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 django.db.models import Q
from django.forms import modelformset_factory
from django.shortcuts import redirect, render
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.translation import ugettext as _
from django.views.decorators.cache import cache_page
from preferences.models import AssoOption, GeneralOption
from re2o.acl import can_create, can_edit, can_view, can_view_all
from re2o.base import SortTable, re2o_paginator
from re2o.utils import all_adherent, all_has_access
from re2o.views import form
from re2o.utils import all_has_access, all_adherent
from re2o.base import re2o_paginator, SortTable
from re2o.acl import can_view, can_view_all, can_edit, can_create
from preferences.models import GeneralOption, AssoOption
from topologie.models import Dormitory, Room
from .forms import DormitoryForm
from .preferences.models import MultiopOption
from topologie.models import Room, Dormitory
def display_rooms_connection(request, dormitory=None):
"""View used to display an overview of the rooms' connection state.
@ -58,9 +54,13 @@ def display_rooms_connection(request, dormitory=None):
dormitory: Dormitory, the dormitory used to filter rooms. If no
dormitory is given, all rooms are displayed (default: None).
"""
room_list = Room.objects.select_related("building__dormitory").filter(
room_list = (
Room.objects.select_related("building__dormitory")
.filter(
building__dormitory__in=MultiopOption.get_cached_value("enabled_dorm").all()
).order_by("building_dormitory", "port")
)
.order_by("building_dormitory", "port")
)
if dormitory:
room_list = room_list.filter(building__dormitory=dormitory)
room_list = SortTable.sort(
@ -113,7 +113,9 @@ def aff_pending_connection(request):
Room.objects.select_related("building__dormitory")
.filter(port__isnull=True)
.filter(adherent__in=all_has_access())
.filter(building__dormitory__in=MultiopOption.get_cached_value("enabled_dorm").all())
.filter(
building__dormitory__in=MultiopOption.get_cached_value("enabled_dorm").all()
)
.order_by("building_dormitory", "port")
)
dormitory_form = DormitoryForm(request.POST or None)
@ -151,7 +153,9 @@ def aff_pending_disconnection(request):
Room.objects.select_related("building__dormitory")
.filter(port__isnull=False)
.exclude(Q(adherent__in=all_has_access()) | Q(adherent__in=all_adherent()))
.filter(building__dormitory__in=MultiopOption.get_cached_value("enabled_dorm").all())
.filter(
building__dormitory__in=MultiopOption.get_cached_value("enabled_dorm").all()
)
.order_by("building_dormitory", "port")
)
dormitory_form = DormitoryForm(request.POST or None)

View file

@ -28,21 +28,10 @@ from __future__ import unicode_literals
from django.contrib import admin
from reversion.admin import VersionAdmin
from .models import (
OptionalUser,
OptionalMachine,
OptionalTopologie,
GeneralOption,
Service,
MailContact,
AssoOption,
MailMessageOption,
HomeOption,
RadiusKey,
SwitchManagementCred,
Reminder,
DocumentTemplate,
)
from .models import (AssoOption, DocumentTemplate, GeneralOption, HomeOption,
MailContact, MailMessageOption, OptionalMachine,
OptionalTopologie, OptionalUser, RadiusKey, Reminder,
Service, SwitchManagementCred)
class OptionalUserAdmin(VersionAdmin):

View file

@ -22,11 +22,12 @@
from rest_framework import serializers
import preferences.models as preferences
from api.serializers import NamespacedHRField, NamespacedHIField, NamespacedHMSerializer
from api.serializers import (NamespacedHIField, NamespacedHMSerializer,
NamespacedHRField)
class OptionalUserSerializer(NamespacedHMSerializer):
"""Serialize `preferences.models.OptionalUser` objects.
"""
"""Serialize `preferences.models.OptionalUser` objects."""
tel_mandatory = serializers.BooleanField(source="is_tel_mandatory")
shell_default = serializers.StringRelatedField()
@ -47,8 +48,7 @@ class OptionalUserSerializer(NamespacedHMSerializer):
class OptionalMachineSerializer(NamespacedHMSerializer):
"""Serialize `preferences.models.OptionalMachine` objects.
"""
"""Serialize `preferences.models.OptionalMachine` objects."""
class Meta:
model = preferences.OptionalMachine
@ -59,13 +59,12 @@ class OptionalMachineSerializer(NamespacedHMSerializer):
"ipv6_mode",
"create_machine",
"ipv6",
"default_dns_ttl"
"default_dns_ttl",
)
class OptionalTopologieSerializer(NamespacedHMSerializer):
"""Serialize `preferences.models.OptionalTopologie` objects.
"""
"""Serialize `preferences.models.OptionalTopologie` objects."""
switchs_management_interface_ip = serializers.CharField()
@ -85,8 +84,7 @@ class OptionalTopologieSerializer(NamespacedHMSerializer):
class RadiusOptionSerializer(NamespacedHMSerializer):
"""Serialize `preferences.models.RadiusOption` objects
"""
"""Serialize `preferences.models.RadiusOption` objects"""
class Meta:
model = preferences.RadiusOption
@ -107,8 +105,7 @@ class RadiusOptionSerializer(NamespacedHMSerializer):
class GeneralOptionSerializer(NamespacedHMSerializer):
"""Serialize `preferences.models.GeneralOption` objects.
"""
"""Serialize `preferences.models.GeneralOption` objects."""
class Meta:
model = preferences.GeneralOption
@ -128,8 +125,7 @@ class GeneralOptionSerializer(NamespacedHMSerializer):
class HomeServiceSerializer(NamespacedHMSerializer):
"""Serialize `preferences.models.Service` objects.
"""
"""Serialize `preferences.models.Service` objects."""
class Meta:
model = preferences.Service
@ -138,8 +134,7 @@ class HomeServiceSerializer(NamespacedHMSerializer):
class AssoOptionSerializer(NamespacedHMSerializer):
"""Serialize `preferences.models.AssoOption` objects.
"""
"""Serialize `preferences.models.AssoOption` objects."""
class Meta:
model = preferences.AssoOption
@ -157,8 +152,7 @@ class AssoOptionSerializer(NamespacedHMSerializer):
class HomeOptionSerializer(NamespacedHMSerializer):
"""Serialize `preferences.models.HomeOption` objects.
"""
"""Serialize `preferences.models.HomeOption` objects."""
class Meta:
model = preferences.HomeOption
@ -166,8 +160,7 @@ class HomeOptionSerializer(NamespacedHMSerializer):
class MailMessageOptionSerializer(NamespacedHMSerializer):
"""Serialize `preferences.models.MailMessageOption` objects.
"""
"""Serialize `preferences.models.MailMessageOption` objects."""
class Meta:
model = preferences.MailMessageOption

View file

@ -21,9 +21,7 @@
from . import views
urls_viewset = [
(r"preferences/service", views.HomeServiceViewSet, "homeservice")
]
urls_viewset = [(r"preferences/service", views.HomeServiceViewSet, "homeservice")]
urls_view = [
(r"preferences/optionaluser", views.OptionalUserView),
@ -33,5 +31,5 @@ urls_view = [
(r"preferences/generaloption", views.GeneralOptionView),
(r"preferences/assooption", views.AssoOptionView),
(r"preferences/homeoption", views.HomeOptionView),
(r"preferences/mailmessageoption", views.MailMessageOptionView)
(r"preferences/mailmessageoption", views.MailMessageOptionView),
]

View file

@ -19,16 +19,16 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from rest_framework import viewsets, generics
from rest_framework import generics, viewsets
from . import serializers
import preferences.models as preferences
from api.permissions import ACLPermission
from . import serializers
class OptionalUserView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.` settings.
"""
"""Exposes details of `preferences.models.` settings."""
permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.OptionalUser.can_view_all]}
@ -39,8 +39,7 @@ class OptionalUserView(generics.RetrieveAPIView):
class OptionalMachineView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.OptionalMachine` settings.
"""
"""Exposes details of `preferences.models.OptionalMachine` settings."""
permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.OptionalMachine.can_view_all]}
@ -51,8 +50,7 @@ class OptionalMachineView(generics.RetrieveAPIView):
class OptionalTopologieView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.OptionalTopologie` settings.
"""
"""Exposes details of `preferences.models.OptionalTopologie` settings."""
permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.OptionalTopologie.can_view_all]}
@ -63,8 +61,7 @@ class OptionalTopologieView(generics.RetrieveAPIView):
class RadiusOptionView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.OptionalTopologie` settings.
"""
"""Exposes details of `preferences.models.OptionalTopologie` settings."""
permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.RadiusOption.can_view_all]}
@ -75,8 +72,7 @@ class RadiusOptionView(generics.RetrieveAPIView):
class GeneralOptionView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.GeneralOption` settings.
"""
"""Exposes details of `preferences.models.GeneralOption` settings."""
permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.GeneralOption.can_view_all]}
@ -87,16 +83,14 @@ class GeneralOptionView(generics.RetrieveAPIView):
class HomeServiceViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `preferences.models.Service` objects.
"""
"""Exposes list and details of `preferences.models.Service` objects."""
queryset = preferences.Service.objects.all()
serializer_class = serializers.HomeServiceSerializer
class AssoOptionView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.AssoOption` settings.
"""
"""Exposes details of `preferences.models.AssoOption` settings."""
permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.AssoOption.can_view_all]}
@ -107,8 +101,7 @@ class AssoOptionView(generics.RetrieveAPIView):
class HomeOptionView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.HomeOption` settings.
"""
"""Exposes details of `preferences.models.HomeOption` settings."""
permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.HomeOption.can_view_all]}
@ -119,8 +112,7 @@ class HomeOptionView(generics.RetrieveAPIView):
class MailMessageOptionView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.MailMessageOption` settings.
"""
"""Exposes details of `preferences.models.MailMessageOption` settings."""
permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.MailMessageOption.can_view_all]}

View file

@ -25,36 +25,22 @@ Forms to edit preferences: users, machines, topology, organisation etc.
from __future__ import unicode_literals
from django.forms import ModelForm, Form
from django.db.models import Q
from django import forms
from django.db.models import Q
from django.forms import Form, ModelForm
from django.utils.translation import ugettext_lazy as _
from re2o.mixins import FormRevMixin
from re2o.widgets import (
AutocompleteModelWidget,
AutocompleteMultipleModelWidget
)
from .models import (
OptionalUser,
OptionalMachine,
OptionalTopologie,
GeneralOption,
AssoOption,
MailMessageOption,
HomeOption,
Service,
MailContact,
Reminder,
RadiusKey,
SwitchManagementCred,
RadiusOption,
CotisationsOption,
DocumentTemplate,
RadiusAttribute,
Mandate,
)
from re2o.widgets import (AutocompleteModelWidget,
AutocompleteMultipleModelWidget)
from topologie.models import Switch
from .models import (AssoOption, CotisationsOption, DocumentTemplate,
GeneralOption, HomeOption, MailContact, MailMessageOption,
Mandate, OptionalMachine, OptionalTopologie, OptionalUser,
RadiusAttribute, RadiusKey, RadiusOption, Reminder,
Service, SwitchManagementCred)
class EditOptionalUserForm(ModelForm):
"""Form used to edit user preferences."""
@ -74,14 +60,22 @@ class EditOptionalUserForm(ModelForm):
self.fields["self_change_shell"].label = _("Self change shell")
self.fields["self_change_pseudo"].label = _("Self change pseudo")
self.fields["self_room_policy"].label = _("Self room policy")
self.fields["local_email_accounts_enabled"].label = _("Local email accounts enabled")
self.fields["local_email_accounts_enabled"].label = _(
"Local email accounts enabled"
)
self.fields["local_email_domain"].label = _("Local email domain")
self.fields["max_email_address"].label = _("Max local email address")
self.fields["delete_notyetactive"].label = _("Delete not yet active users")
self.fields["disable_emailnotyetconfirmed"].label = _("Disabled email not yet confirmed")
self.fields["disable_emailnotyetconfirmed"].label = _(
"Disabled email not yet confirmed"
)
self.fields["self_adhesion"].label = _("Self registration")
self.fields["all_users_active"].label = _("All users are state active by default")
self.fields["allow_set_password_during_user_creation"].label = _("Allow set password during user creation")
self.fields["all_users_active"].label = _(
"All users are state active by default"
)
self.fields["allow_set_password_during_user_creation"].label = _(
"Allow set password during user creation"
)
self.fields["allow_archived_connexion"].label = _("Allow archived connexion")
@ -211,9 +205,7 @@ class EditMailMessageOptionForm(ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(EditMailMessageOptionForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields["welcome_mail_fr"].label = _(
"Message for the French welcome email"
)
self.fields["welcome_mail_fr"].label = _("Message for the French welcome email")
self.fields["welcome_mail_en"].label = _(
"Message for the English welcome email"
)
@ -355,8 +347,7 @@ class ServiceForm(ModelForm):
class DelServiceForm(Form):
"""Form used to delete one or several services displayed on the home page.
"""
"""Form used to delete one or several services displayed on the home page."""
services = forms.ModelMultipleChoiceField(
queryset=Service.objects.none(),

View file

@ -25,23 +25,22 @@ Models defining the preferences for users, machines, emails, general settings
etc.
"""
from __future__ import unicode_literals
import os
from django.utils.functional import cached_property
from django.utils import timezone
import os
from datetime import timedelta
from django.core.cache import cache
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.core.cache import cache
from django.forms import ValidationError
from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
import machines.models
from re2o.mixins import AclMixin, RevMixin
from re2o.aes_field import AESEncryptedField
from datetime import timedelta
from re2o.mixins import AclMixin, RevMixin
class PreferencesModel(models.Model):
@ -328,7 +327,7 @@ class OptionalTopologie(AclMixin, PreferencesModel):
configuration.
"""
if self.switchs_ip_type:
from machines.models import Role, Interface
from machines.models import Interface, Role
return (
Interface.objects.filter(
@ -364,14 +363,14 @@ class OptionalTopologie(AclMixin, PreferencesModel):
"""Get the dictionary of IP addresses for the configuration of
switches.
"""
from machines.models import Role, Ipv6List, Interface
from machines.models import Interface, Ipv6List, Role
def return_ips_dict(interfaces):
return {
"ipv4": [str(interface.ipv4) for interface in interfaces],
"ipv6": Ipv6List.objects.filter(interface__in=interfaces).filter(active=True).values_list(
"ipv6", flat=True
),
"ipv6": Ipv6List.objects.filter(interface__in=interfaces)
.filter(active=True)
.values_list("ipv6", flat=True),
}
ntp_servers = Role.all_interfaces_for_roletype("ntp-server").filter(
@ -660,7 +659,7 @@ class Mandate(RevMixin, AclMixin, models.Model):
@classmethod
def get_mandate(cls, date=timezone.now):
""""Get the mandate taking place at the given date.
""" "Get the mandate taking place at the given date.
Args:
date: the date used to find the mandate (default: timezone.now).

View file

@ -79,9 +79,7 @@ urlpatterns = [
name="edit-options",
),
path("add_service", views.add_service, name="add-service"),
path(
"edit_service/<int:serviceid>", views.edit_service, name="edit-service"
),
path("edit_service/<int:serviceid>", views.edit_service, name="edit-service"),
path("del_service/<int:serviceid>", views.del_service, name="del-service"),
path("add_mailcontact", views.add_mailcontact, name="add-mailcontact"),
path(
@ -143,13 +141,9 @@ urlpatterns = [
name="del-document-template",
),
path("add_mandate", views.add_mandate, name="add-mandate"),
path(
"edit_mandate/<int:mandateid>", views.edit_mandate, name="edit-mandate"
),
path("edit_mandate/<int:mandateid>", views.edit_mandate, name="edit-mandate"),
path("del_mandate/<int:mandateid>", views.del_mandate, name="del-mandate"),
path(
"add_radiusattribute", views.add_radiusattribute, name="add-radiusattribute"
),
path("add_radiusattribute", views.add_radiusattribute, name="add-radiusattribute"),
path(
"edit_radiusattribute/<int:radiusattributeid>",
views.edit_radiusattribute,

View file

@ -30,61 +30,33 @@ services etc.)
from __future__ import unicode_literals
from django.urls import reverse
from django.shortcuts import redirect
from importlib import import_module
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.db.models import ProtectedError
from django.db import transaction
from django.db.models import ProtectedError
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.translation import ugettext as _
from reversion import revisions as reversion
from importlib import import_module
from re2o.acl import (acl_error_message, can_create, can_delete,
can_delete_set, can_edit, can_view_all)
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,
acl_error_message,
)
from .forms import MailContactForm, DelMailContactForm
from .forms import (
ServiceForm,
ReminderForm,
RadiusKeyForm,
SwitchManagementCredForm,
DocumentTemplateForm,
DelDocumentTemplateForm,
RadiusAttributeForm,
DelRadiusAttributeForm,
MandateForm,
)
from .models import (
Service,
MailContact,
OptionalUser,
OptionalMachine,
AssoOption,
MailMessageOption,
GeneralOption,
OptionalTopologie,
HomeOption,
Reminder,
RadiusKey,
SwitchManagementCred,
RadiusOption,
CotisationsOption,
DocumentTemplate,
RadiusAttribute,
Mandate,
)
from . import models
from . import forms
from . import forms, models
from .forms import (DelDocumentTemplateForm, DelMailContactForm,
DelRadiusAttributeForm, DocumentTemplateForm,
MailContactForm, MandateForm, RadiusAttributeForm,
RadiusKeyForm, ReminderForm, ServiceForm,
SwitchManagementCredForm)
from .models import (AssoOption, CotisationsOption, DocumentTemplate,
GeneralOption, HomeOption, MailContact, MailMessageOption,
Mandate, OptionalMachine, OptionalTopologie, OptionalUser,
RadiusAttribute, RadiusKey, RadiusOption, Reminder,
Service, SwitchManagementCred)
def edit_options_template_function(request, section, forms, models):

View file

@ -30,8 +30,8 @@ from __future__ import unicode_literals
import sys
from itertools import chain
from django.db.models import Model
from django.contrib import messages
from django.db.models import Model
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.translation import ugettext as _

View file

@ -28,22 +28,22 @@ Module defining a AESEncryptedField object that can be used in forms
to handle the use of properly encrypting and decrypting AES keys
"""
import string
import binascii
import string
from random import choice
from Crypto.Cipher import AES
from django.db import models
from Crypto.Cipher import AES
from django import forms
from django.conf import settings
from django.db import models
EOD_asbyte = b"`%EofD%`" # This should be something that will not occur in strings
EOD = EOD_asbyte.decode("utf-8")
def genstring(length=16, chars=string.printable):
""" Generate a random string of length `length` and composed of
the characters in `chars` """
"""Generate a random string of length `length` and composed of
the characters in `chars`"""
return "".join([choice(chars) for i in range(length)])
@ -71,8 +71,8 @@ class AESEncryptedFormField(forms.CharField):
class AESEncryptedField(models.CharField):
""" A Field that can be used in forms for adding the support
of AES ecnrypted fields """
"""A Field that can be used in forms for adding the support
of AES ecnrypted fields"""
def save_form_data(self, instance, data):
setattr(

View file

@ -26,12 +26,11 @@ Global independant usefull functions
import smtplib
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
from django.utils.translation import ugettext_lazy as _
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from re2o.settings import EMAIL_HOST
# Mapping of srtftime format for better understanding
# https://docs.python.org/3.6/library/datetime.html#strftime-strptime-behavior
datetime_mapping = {
@ -108,9 +107,9 @@ def get_input_formats_help_text(input_formats):
class SortTable:
""" Class gathering uselful stuff to sort the colums of a table, according
"""Class gathering uselful stuff to sort the colums of a table, according
to the column and order requested. It's used with a dict of possible
values and associated model_fields """
values and associated model_fields"""
# All the possible possible values
# The naming convention is based on the URL or the views function
@ -228,8 +227,8 @@ class SortTable:
@staticmethod
def sort(request, col, order, values):
""" Check if the given values are possible and add .order_by() and
a .reverse() as specified according to those values """
"""Check if the given values are possible and add .order_by() and
a .reverse() as specified according to those values"""
fields = values.get(col, None)
if not fields:
fields = values.get("default", [])

View file

@ -24,13 +24,14 @@
from __future__ import unicode_literals
import datetime
from importlib import import_module
from django.contrib import messages
from django.contrib.messages import get_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 preferences.models import GeneralOption, OptionalMachine
from re2o.settings_local import OPTIONNAL_APPS_RE2O
@ -51,7 +52,9 @@ def context_user(request):
if global_message not in [msg.message for msg in get_messages(request)]:
messages.warning(request, global_message)
else:
if global_message not in [msg.message for msg in get_messages(request._request)]:
if global_message not in [
msg.message for msg in get_messages(request._request)
]:
messages.warning(request._request, global_message)
if user.is_authenticated:
interfaces = user.user_interfaces()

View file

@ -4,44 +4,44 @@ A list of the proud contributors to Re2o
"""
CONTRIBUTORS = [
'Gabriel Detraz',
'Hugo Levy-falk',
'Maël Kervella',
'Jean-romain Garnier',
'Arthur Grisel-davy',
'Laouen Fernet',
'Augustin Lemesle',
'Lara Kermarec',
'Root `root` Root',
'Alexandre Iooss',
'Yoann Piétri',
'Charlie Jacomme',
'Corentin Canebier',
'Bombar Maxime',
'Guillaume Goessel',
'Matthieu Michelet',
'Edpibu',
'Fardale',
'Jean-marie Mineau',
'David Sinquin',
'Gabriel Le Bouder',
'Simon Brélivet',
'~anonymised~',
'Benjamin Graillot',
'Leïla Bekaddour',
'Éloi Alain',
'Pierre Cadart',
'Antoine Vintache',
'Thibault De Boutray',
'Delphine Salvy',
'Joanne Steiner',
'Krokmou',
'B',
'Daniel Stan',
'Gwenael Le Hir',
'Hugo Hervieux',
'Mikachu',
'Nymous',
'Pierre-antoine Comby',
'Vincent Le Gallic',
"Gabriel Detraz",
"Hugo Levy-falk",
"Maël Kervella",
"Jean-romain Garnier",
"Arthur Grisel-davy",
"Laouen Fernet",
"Augustin Lemesle",
"Lara Kermarec",
"Root `root` Root",
"Alexandre Iooss",
"Yoann Piétri",
"Charlie Jacomme",
"Corentin Canebier",
"Bombar Maxime",
"Guillaume Goessel",
"Matthieu Michelet",
"Edpibu",
"Fardale",
"Jean-marie Mineau",
"David Sinquin",
"Gabriel Le Bouder",
"Simon Brélivet",
"~anonymised~",
"Benjamin Graillot",
"Leïla Bekaddour",
"Éloi Alain",
"Pierre Cadart",
"Antoine Vintache",
"Thibault De Boutray",
"Delphine Salvy",
"Joanne Steiner",
"Krokmou",
"B",
"Daniel Stan",
"Gwenael Le Hir",
"Hugo Hervieux",
"Mikachu",
"Nymous",
"Pierre-antoine Comby",
"Vincent Le Gallic",
]

View file

@ -40,8 +40,8 @@ class FieldPermissionModelMixin:
FIELD_PERMISSION_MISSING_DEFAULT = True
def has_field_perm(self, user, field):
""" Checks if a `user` has the right to edit the `field`
of this model """
"""Checks if a `user` has the right to edit the `field`
of this model"""
if field in self.field_permissions:
checks = self.field_permissions[field]
if not isinstance(checks, (list, tuple)):

View file

@ -32,12 +32,12 @@ import binascii
import crypt
import hashlib
import os
from base64 import encodestring, decodestring, b64encode, b64decode
from base64 import b64decode, b64encode, decodestring, encodestring
from collections import OrderedDict
from django.contrib.auth import hashers
from django.contrib.auth.backends import ModelBackend
from hmac import compare_digest as constant_time_compare
from django.contrib.auth import hashers
from django.contrib.auth.backends import ModelBackend
ALGO_NAME = "{SSHA}"
ALGO_LEN = len(ALGO_NAME + "$")
@ -45,7 +45,7 @@ DIGEST_LEN = 20
def makeSecret(password):
""" Build a hashed and salted version of the password with SSHA
"""Build a hashed and salted version of the password with SSHA
Parameters:
password (string): Password to hash
@ -60,7 +60,7 @@ def makeSecret(password):
def hashNT(password):
""" Build a md4 hash of the password to use as the NT-password
"""Build a md4 hash of the password to use as the NT-password
Parameters:
password (string): Password to hash
@ -93,7 +93,7 @@ def checkPassword(challenge_password, password):
def hash_password_salt(hashed_password):
""" Extract the salt from a given hashed password
"""Extract the salt from a given hashed password
Parameters:
hashed_password (string): Hashed password to extract salt
@ -285,7 +285,9 @@ class RecryptBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
# we obtain from the classical auth backend the user
user = super(RecryptBackend, self).authenticate(request, username, password, **kwargs)
user = super(RecryptBackend, self).authenticate(
request, username, password, **kwargs
)
if user:
if not (user.pwd_ntlm):
# if we dont have NT hash, we create it

View file

@ -25,11 +25,13 @@
All functions linked with emails here. Non model or app dependant
"""
from django.utils.translation import ugettext_lazy as _
from django.core.mail import send_mail as django_send_mail
from django.contrib import messages
from smtplib import SMTPException
from socket import herror, gaierror
from socket import gaierror, herror
from django.contrib import messages
from django.core.mail import send_mail as django_send_mail
from django.utils.translation import ugettext_lazy as _
def send_mail(request, *args, **kwargs):
"""Wrapper for Django's send_mail which handles errors"""
@ -39,7 +41,8 @@ def send_mail(request, *args, **kwargs):
except (SMTPException, ConnectionError, herror, gaierror) as e:
messages.error(
request,
_("Failed to send email: %(error)s.") % {
_("Failed to send email: %(error)s.")
% {
"error": e,
},
)
@ -53,7 +56,8 @@ def send_mail_object(mail, request):
if request:
messages.error(
request,
_("Failed to send email: %(error)s.") % {
_("Failed to send email: %(error)s.")
% {
"error": e,
},
)

View file

@ -25,6 +25,7 @@ commits. This list is extracted from the current gitlab repository.
"""
import os
from django.core.management.base import BaseCommand

View file

@ -23,9 +23,9 @@
A set of mixins used all over the project to avoid duplicating code
"""
from reversion import revisions as reversion
from django.db import transaction
from django.utils.translation import ugettext as _
from reversion import revisions as reversion
class RevMixin(object):
@ -252,4 +252,3 @@ class AclMixin(object):
else None,
(permission,),
)

View file

@ -24,17 +24,16 @@ with Re2o throught the CLI
"""
import os
from os.path import dirname
import sys
import pwd
import sys
from getpass import getpass
from reversion import revisions as reversion
from os.path import dirname
from django.core.wsgi import get_wsgi_application
from django.core.management.base import CommandError
from django.core.wsgi import get_wsgi_application
from django.db import transaction
from django.utils.html import strip_tags
from reversion import revisions as reversion
from users.models import User
@ -60,15 +59,12 @@ def get_user(pseudo):
if len(user) == 0:
raise CommandError("Invalid user.")
if len(user) > 1:
raise CommandError(
"Several users match this username. This SHOULD NOT happen."
)
raise CommandError("Several users match this username. This SHOULD NOT happen.")
return user[0]
def get_system_user():
"""Find the system user login who used the command
"""
"""Find the system user login who used the command"""
return pwd.getpwuid(int(os.getenv("SUDO_UID") or os.getuid())).pw_name
@ -105,6 +101,4 @@ def form_cli(Form, user, action, *args, **kwargs):
reversion.set_user(user)
reversion.set_comment(action)
sys.stdout.write(
"%s: done. The edit may take several minutes to apply.\n" % action
)
sys.stdout.write("%s: done. The edit may take several minutes to apply.\n" % action)

View file

@ -36,7 +36,9 @@ https://docs.djangoproject.com/en/1.8/ref/settings/
from __future__ import unicode_literals
import os
from .settings_default import *
try:
from .settings_local import *
except ImportError:
@ -60,7 +62,11 @@ LOGIN_REDIRECT_URL = "/" # The URL for redirecting after login
# Application definition
# dal_legacy_static only needed for Django < 2.0 (https://django-autocomplete-light.readthedocs.io/en/master/install.html#django-versions-earlier-than-2-0)
EARLY_EXTERNAL_CONTRIB_APPS = ("dal", "dal_select2", "dal_legacy_static") # Need to be added before django.contrib.admin (https://django-autocomplete-light.readthedocs.io/en/master/install.html#configuration)
EARLY_EXTERNAL_CONTRIB_APPS = (
"dal",
"dal_select2",
"dal_legacy_static",
) # Need to be added before django.contrib.admin (https://django-autocomplete-light.readthedocs.io/en/master/install.html#configuration)
DJANGO_CONTRIB_APPS = (
"django.contrib.admin",
"django.contrib.auth",
@ -82,7 +88,11 @@ LOCAL_APPS = (
"logs",
)
INSTALLED_APPS = (
EARLY_EXTERNAL_CONTRIB_APPS + DJANGO_CONTRIB_APPS + EXTERNAL_CONTRIB_APPS + LOCAL_APPS + OPTIONNAL_APPS
EARLY_EXTERNAL_CONTRIB_APPS
+ DJANGO_CONTRIB_APPS
+ EXTERNAL_CONTRIB_APPS
+ LOCAL_APPS
+ OPTIONNAL_APPS
)
MIDDLEWARE = (
"django.middleware.security.SecurityMiddleware",
@ -196,16 +206,16 @@ EMAIL_TIMEOUT = 10
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]

View file

@ -65,24 +65,24 @@ OPTIONNAL_APPS_RE2O = ()
# Some Django apps you want to add in you local project
OPTIONNAL_APPS = OPTIONNAL_APPS_RE2O + ()
#Set auth password validator
# Set auth password validator
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
'OPTIONS': {
'user_attributes': ['surname', 'pseudo', 'name', 'email'],
}
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
"OPTIONS": {
"user_attributes": ["surname", "pseudo", "name", "email"],
},
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {
'min_length': 8,
}
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
"OPTIONS": {
"min_length": 8,
},
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]

View file

@ -114,24 +114,24 @@ OPTIONNAL_APPS_RE2O = ()
# Some Django apps you want to add in you local project
OPTIONNAL_APPS = OPTIONNAL_APPS_RE2O + ()
#Set auth password validator
# Set auth password validator
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
'OPTIONS': {
'user_attributes': ['surname', 'pseudo', 'name', 'email'],
}
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
"OPTIONS": {
"user_attributes": ["surname", "pseudo", "name", "email"],
},
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {
'min_length': 8,
}
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
"OPTIONS": {
"min_length": 8,
},
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]

View file

@ -73,9 +73,8 @@ an instance of a model (either Model.can_xxx or instance.can_xxx)
import sys
from django import template
from django.template.base import Node, NodeList
from django.contrib.contenttypes.models import ContentType
from django.template.base import Node, NodeList
register = template.Library()
@ -202,7 +201,7 @@ def acl_fct(callback, reverse):
@register.tag("cannot_edit_history")
def acl_history_filter(parser, token):
"""Templatetag for acl checking on history."""
tag_name, = token.split_contents()
(tag_name,) = token.split_contents()
callback = get_callback(tag_name)
oknodes = parser.parse(("acl_else", "acl_end"))

View file

@ -20,10 +20,12 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from django import template
from .url_insert_param import url_insert_param
register = template.Library()
@register.simple_tag
def pagination_insert_page_and_id(url, page=1, id=None, **kwargs):
"""
@ -77,10 +79,14 @@ def pagination_insert_page_and_id(url, page=1, id=None, **kwargs):
"""
page_arg = "page"
if "page_arg" in kwargs and kwargs["page_arg"] is not None and len(kwargs["page_arg"]) > 0:
if (
"page_arg" in kwargs
and kwargs["page_arg"] is not None
and len(kwargs["page_arg"]) > 0
):
page_arg = kwargs["page_arg"]
args = { "url": url, page_arg: page}
args = {"url": url, page_arg: page}
new_url = url_insert_param(**args)
if id != None:

View file

@ -26,8 +26,8 @@ which indicated if a user can creates an account by himself
"""
from django import template
from preferences.models import OptionalUser
from preferences.models import OptionalUser
register = template.Library()

View file

@ -24,14 +24,13 @@
from __future__ import unicode_literals
from django.conf import settings
from django.urls import include, path
from django.contrib import admin
from django.urls import include, path
from django.utils.translation import ugettext_lazy as _
from django.views.generic import RedirectView
from .settings_local import OPTIONNAL_APPS_RE2O
from .views import index, about_page, contact_page, handler404, handler500
from .views import about_page, contact_page, handler404, handler500, index
# Admin site configuration
admin.site.index_title = _("Homepage")

View file

@ -36,14 +36,14 @@ Functions:
from __future__ import unicode_literals
from django.utils import timezone
from django.contrib.auth.models import Group, Permission
from django.db.models import Q
from django.contrib.auth.models import Permission, Group
from django.utils import timezone
from cotisations.models import Cotisation, Facture, Vente
from machines.models import Interface, Machine
from users.models import Adherent, User, Ban, Whitelist
from preferences.models import AssoOption
from users.models import Adherent, Ban, User, Whitelist
def get_group_having_permission(*permission_name):
@ -83,7 +83,9 @@ def filter_results(query_filter, dormitory, user_type):
- on user_type (adherent or club) is specified
Returns the filter"""
if dormitory:
query_filter &= (Q(adherent__room__building__dormitory=dormitory) | Q(club__room__building__dormitory=dormitory))
query_filter &= Q(adherent__room__building__dormitory=dormitory) | Q(
club__room__building__dormitory=dormitory
)
if user_type == "adherent":
query_filter &= Q(adherent__isnull=False)
if user_type == "club":
@ -91,7 +93,9 @@ def filter_results(query_filter, dormitory, user_type):
return query_filter
def all_adherent(search_time=None, including_asso=True, dormitory=None, user_type="all"):
def all_adherent(
search_time=None, including_asso=True, dormitory=None, user_type="all"
):
"""Return all people who have a valid membership at org. Optimised to make only one
sql query. Build a filter and then apply it to User. Check for each user if a valid
membership is registered at the desired search_time.
@ -110,9 +114,9 @@ def all_adherent(search_time=None, including_asso=True, dormitory=None, user_typ
filter_user = Q(
facture__in=Facture.objects.filter(
vente__cotisation__in=Cotisation.objects.filter(
Q(vente__facture__facture__valid=True) &
Q(date_start_memb__lt=search_time) &
Q(date_end_memb__gt=search_time)
Q(vente__facture__facture__valid=True)
& Q(date_start_memb__lt=search_time)
& Q(date_end_memb__gt=search_time)
)
)
)
@ -191,9 +195,9 @@ def all_conn(search_time=None, including_asso=True, dormitory=None, user_type="a
filter_user = Q(
facture__in=Facture.objects.filter(
vente__cotisation__in=Cotisation.objects.filter(
Q(vente__facture__facture__valid=True) &
Q(date_start_con__lt=search_time) &
Q(date_end_con__gt=search_time)
Q(vente__facture__facture__valid=True)
& Q(date_start_con__lt=search_time)
& Q(date_end_con__gt=search_time)
)
)
)
@ -205,7 +209,9 @@ def all_conn(search_time=None, including_asso=True, dormitory=None, user_type="a
return User.objects.filter(filter_user).distinct()
def all_has_access(search_time=None, including_asso=True, dormitory=None, user_type="all"):
def all_has_access(
search_time=None, including_asso=True, dormitory=None, user_type="all"
):
"""Return all people who have an valid internet access at org. Call previously buid filters.
Can't do that in one sql query unfortunatly. Apply each filters, and return users
with a whitelist, or a valid paid access, except banned users.
@ -221,9 +227,8 @@ def all_has_access(search_time=None, including_asso=True, dormitory=None, user_t
"""
if search_time is None:
search_time = timezone.now()
filter_user = (
Q(state=User.STATE_ACTIVE)
& ~Q(email_state=User.EMAIL_STATE_UNVERIFIED)
filter_user = Q(state=User.STATE_ACTIVE) & ~Q(
email_state=User.EMAIL_STATE_UNVERIFIED
)
if including_asso:
asso_user = AssoOption.get_cached_value("utilisateur_asso")
@ -231,18 +236,36 @@ def all_has_access(search_time=None, including_asso=True, dormitory=None, user_t
filter_user |= Q(id=asso_user.id)
filter_user = filter_results(filter_user, dormitory, user_type)
return User.objects.filter(
Q(filter_user) & (
Q(filter_user)
& (
Q(
id__in=all_whitelisted(search_time=search_time, dormitory=dormitory, user_type=user_type)
) | (
Q(
id__in=all_adherent(search_time=search_time, including_asso=including_asso, dormitory=dormitory, user_type=user_type)
) & Q(
id__in=all_conn(search_time=search_time, including_asso=including_asso, dormitory=dormitory, user_type=user_type)
id__in=all_whitelisted(
search_time=search_time, dormitory=dormitory, user_type=user_type
)
)
) & ~Q(
id__in=all_baned(search_time=search_time, dormitory=dormitory, user_type=user_type)
| (
Q(
id__in=all_adherent(
search_time=search_time,
including_asso=including_asso,
dormitory=dormitory,
user_type=user_type,
)
)
& Q(
id__in=all_conn(
search_time=search_time,
including_asso=including_asso,
dormitory=dormitory,
user_type=user_type,
)
)
)
)
& ~Q(
id__in=all_baned(
search_time=search_time, dormitory=dormitory, user_type=user_type
)
)
).distinct()

View file

@ -26,28 +26,22 @@ Welcom main page view, and several template widely used in re2o views
from __future__ import unicode_literals
import git
from importlib import import_module
import git
from dal import autocomplete
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import render
from django.template.context_processors import csrf
from django.conf import settings
from django.utils.translation import ugettext as _
from django.contrib.auth.mixins import LoginRequiredMixin
from django.utils.decorators import method_decorator
from dal import autocomplete
from django.utils.translation import ugettext as _
from preferences.models import (
Service,
MailContact,
AssoOption,
HomeOption,
GeneralOption,
Mandate,
)
from preferences.models import (AssoOption, GeneralOption, HomeOption,
MailContact, Mandate, Service)
from re2o.settings_local import OPTIONNAL_APPS_RE2O
from .contributors import CONTRIBUTORS
from importlib import import_module
from re2o.settings_local import OPTIONNAL_APPS_RE2O
def form(ctx, template, request):
@ -95,9 +89,9 @@ def index(request):
def about_page(request):
""" The view for the about page.
"""The view for the about page.
Fetch some info about the configuration of the project. If it can't
get the info from the Git repository, fallback to default string """
get the info from the Git repository, fallback to default string"""
option = AssoOption.objects.get()
general = GeneralOption.objects.get()
git_info_contributors = CONTRIBUTORS
@ -197,4 +191,3 @@ class AutocompleteLoggedOutViewMixin(autocomplete.Select2QuerySetView):
class AutocompleteViewMixin(LoginRequiredMixin, AutocompleteLoggedOutViewMixin):
pass

View file

@ -25,12 +25,12 @@ Re2o Forms and ModelForms Widgets.
Used in others forms for using autocomplete engine.
"""
from django.utils.translation import ugettext as _
from dal import autocomplete
from django.utils.translation import ugettext as _
class AutocompleteModelWidget(autocomplete.ModelSelect2):
""" A mixin subclassing django-autocomplete-light's Select2 model to pass default options
"""A mixin subclassing django-autocomplete-light's Select2 model to pass default options
See https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#passing-options-to-select2
"""
@ -54,7 +54,7 @@ class AutocompleteModelWidget(autocomplete.ModelSelect2):
class AutocompleteMultipleModelWidget(autocomplete.ModelSelect2Multiple):
""" A mixin subclassing django-autocomplete-light's Select2 model to pass default options
"""A mixin subclassing django-autocomplete-light's Select2 model to pass default options
See https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#passing-options-to-select2
"""

View file

@ -32,12 +32,11 @@ https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/
from __future__ import unicode_literals
import os
from os.path import dirname
import sys
from os.path import dirname
from django.core.wsgi import get_wsgi_application
sys.path.append(dirname(dirname(__file__)))
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "re2o.settings")

View file

@ -28,19 +28,16 @@ Gplv2"""
from __future__ import unicode_literals
from django.db.models import Q, Value
from django.db.models.functions import Concat
from netaddr import EUI, AddrFormatError
from django.db.models import Q
from django.db.models import Value
from django.db.models.functions import Concat
from users.models import User, Adherent, Club, Ban, Whitelist
from machines.models import Machine
from topologie.models import Port, Switch, Room
from cotisations.models import Facture
from machines.models import Machine
from preferences.models import GeneralOption
from re2o.base import SortTable, re2o_paginator
from topologie.models import Port, Room, Switch
from users.models import Adherent, Ban, Club, User, Whitelist
# List of fields the search applies to
FILTER_FIELDS = [

View file

@ -27,6 +27,7 @@ from __future__ import unicode_literals
from django import forms
from django.forms import Form
from django.utils.translation import ugettext_lazy as _
from re2o.base import get_input_formats_help_text
CHOICES_USER = (

View file

@ -28,24 +28,18 @@ Gplv2"""
from __future__ import unicode_literals
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from users.models import User
from cotisations.models import Cotisation
from machines.models import Machine
from search.forms import (
SearchForm,
SearchFormPlus,
CHOICES_USER,
CHOICES_EMAILS,
CHOICES_AFF,
initial_choices,
)
from re2o.acl import can_view_all
from search.forms import (CHOICES_AFF, CHOICES_EMAILS, CHOICES_USER,
SearchForm, SearchFormPlus, initial_choices)
from users.models import User
from .engine import empty_filters, create_queries, search_single_query
from .engine import apply_filters, finish_results
from .engine import (apply_filters, create_queries, empty_filters,
finish_results, search_single_query)
def get_results(query, request, params):
@ -70,10 +64,7 @@ def get_results(query, request, params):
results = apply_filters(filters, request.user, aff)
results = finish_results(
request,
results,
request.GET.get("col"),
request.GET.get("order")
request, results, request.GET.get("col"), request.GET.get("order")
)
results.update({"search_term": query})
@ -90,9 +81,7 @@ def search(request):
request,
"search/index.html",
get_results(
search_form.cleaned_data.get("q", ""),
request,
search_form.cleaned_data
search_form.cleaned_data.get("q", ""), request, search_form.cleaned_data
),
)
return render(request, "search/search.html", {"search_form": search_form})
@ -108,9 +97,7 @@ def searchp(request):
request,
"search/index.html",
get_results(
search_form.cleaned_data.get("q", ""),
request,
search_form.cleaned_data
search_form.cleaned_data.get("q", ""), request, search_form.cleaned_data
),
)
return render(request, "search/search.html", {"search_form": search_form})

View file

@ -23,13 +23,14 @@
"""Defines the custom runners for Re2o.
"""
import volatildap
import os.path
from django.test.runner import DiscoverRunner
import volatildap
from django.conf import settings
from django.test.runner import DiscoverRunner
from users.models import LdapUser, LdapUserGroup, LdapServiceUser, LdapServiceUserGroup
from users.models import (LdapServiceUser, LdapServiceUserGroup, LdapUser,
LdapUserGroup)
# The path of this file
__here = os.path.dirname(os.path.realpath(__file__))

View file

@ -25,15 +25,18 @@ Ticket preferences model
from django.contrib import admin
from .models import Ticket, CommentTicket
from reversion.admin import VersionAdmin
from .models import CommentTicket, Ticket
class TicketAdmin(VersionAdmin):
pass
class CommentTicketAdmin(VersionAdmin):
pass
admin.site.register(Ticket, TicketAdmin)
admin.site.register(CommentTicket, CommentTicketAdmin)

View file

@ -25,14 +25,15 @@ Ticket form
from django import forms
from django.forms import Form, ModelForm
from django.template.loader import render_to_string
from django.forms import ModelForm, Form
from django.utils.translation import ugettext_lazy as _
from re2o.field_permissions import FieldPermissionFormMixin
from re2o.mixins import FormRevMixin
from re2o.widgets import AutocompleteModelWidget
from django.utils.translation import ugettext_lazy as _
from .models import Ticket, CommentTicket
from .models import CommentTicket, Ticket
class NewTicketForm(FormRevMixin, ModelForm):
@ -46,9 +47,11 @@ class NewTicketForm(FormRevMixin, ModelForm):
request = kwargs.pop("request", None)
super(NewTicketForm, self).__init__(*args, **kwargs)
if request.user.is_authenticated:
self.fields.pop('email')
self.fields.pop("email")
self.instance.user = request.user
self.fields['description'].help_text = render_to_string('tickets/help_text.html')
self.fields["description"].help_text = render_to_string(
"tickets/help_text.html"
)
self.instance.language = getattr(request, "LANGUAGE_CODE", "en")
self.instance.request = request
@ -67,7 +70,7 @@ class EditTicketForm(FormRevMixin, ModelForm):
def __init__(self, *args, **kwargs):
super(EditTicketForm, self).__init__(*args, **kwargs)
self.fields['email'].required = False
self.fields["email"].required = False
class CommentTicketForm(FormRevMixin, ModelForm):
@ -83,4 +86,3 @@ class CommentTicketForm(FormRevMixin, ModelForm):
super(CommentTicketForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields["comment"].label = _("comment")
self.instance.request = request

View file

@ -25,22 +25,19 @@ Ticket model
from __future__ import absolute_import
from django.core.mail import EmailMessage
from django.db import models
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.template import loader
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from reversion.models import Version
from re2o.mixins import AclMixin
from re2o.mail_utils import send_mail_object
from django.core.mail import EmailMessage
from preferences.models import GeneralOption
import users.models
from preferences.models import GeneralOption
from re2o.mail_utils import send_mail_object
from re2o.mixins import AclMixin
from .preferences.models import TicketOption
@ -96,7 +93,9 @@ class Ticket(AclMixin, models.Model):
def __str__(self):
if self.user:
return _("Ticket from {name}. Date: {date}.").format(name=self.user.get_full_name(),date=self.date)
return _("Ticket from {name}. Date: {date}.").format(
name=self.user.get_full_name(), date=self.date
)
else:
return _("Anonymous ticket. Date: %s.") % (self.date)
@ -137,7 +136,6 @@ class Ticket(AclMixin, models.Model):
)
send_mail_object(mail_to_send, self.request)
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."""
@ -189,9 +187,7 @@ class CommentTicket(AclMixin, models.Model):
blank=False,
null=False,
)
parent_ticket = models.ForeignKey(
"Ticket", on_delete=models.CASCADE
)
parent_ticket = models.ForeignKey("Ticket", on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(
"users.User",
@ -207,7 +203,12 @@ class CommentTicket(AclMixin, models.Model):
@cached_property
def comment_id(self):
return CommentTicket.objects.filter(parent_ticket=self.parent_ticket, pk__lt=self.pk).count() + 1
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
@ -218,7 +219,9 @@ class CommentTicket(AclMixin, models.Model):
):
return (
False,
_("You don't have the right to view other tickets comments than yours."),
_(
"You don't have the right to view other tickets comments than yours."
),
("tickets.view_commentticket",),
)
else:
@ -227,13 +230,15 @@ class CommentTicket(AclMixin, models.Model):
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.change_commentticket")
and (self.parent_ticket.user != user_request or self.parent_ticket.user != self.created_by)
if not user_request.has_perm("tickets.change_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."),
_(
"You don't have the right to edit other tickets comments than yours."
),
("tickets.change_commentticket",),
)
else:

View file

@ -24,10 +24,11 @@ Ticket preferences form
"""
from django import forms
from django.forms import ModelForm, Form
from django.forms import Form, ModelForm
from django.utils.translation import ugettext_lazy as _
from re2o.mixins import FormRevMixin
from .models import TicketOption

View file

@ -27,8 +27,8 @@ Ticket preferences model
from django.db import models
from django.utils.translation import ugettext_lazy as _
from re2o.mixins import AclMixin, RevMixin
from preferences.models import PreferencesModel
from re2o.mixins import AclMixin, RevMixin
class TicketOption(AclMixin, PreferencesModel):

View file

@ -26,19 +26,16 @@
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
from django.shortcuts import redirect, render
from django.template.loader import render_to_string
from django.utils.translation import ugettext as _
from django.urls import reverse
from re2o.base import re2o_paginator
from re2o.acl import can_view, can_view_all, can_edit, can_create
from django.utils.translation import ugettext as _
from preferences.views import edit_options_template_function
from re2o.acl import can_create, can_edit, can_view, can_view_all
from re2o.base import re2o_paginator
from . import forms
from . import models
from . import forms, models
def aff_preferences(request):

Some files were not shown because too many files have changed in this diff Show more