mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-12-23 07:23:46 +00:00
style: 🎨 Apply black and isort
This commit is contained in:
parent
6736caf9f4
commit
ec2b4afd41
141 changed files with 1687 additions and 1942 deletions
|
@ -8,4 +8,4 @@ from django.apps import AppConfig
|
||||||
class ApiConfig(AppConfig):
|
class ApiConfig(AppConfig):
|
||||||
"""Configuration of api app."""
|
"""Configuration of api app."""
|
||||||
|
|
||||||
name = "api"
|
name = "api"
|
||||||
|
|
|
@ -31,12 +31,10 @@ from rest_framework.authentication import TokenAuthentication
|
||||||
|
|
||||||
|
|
||||||
class ExpiringTokenAuthentication(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):
|
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)
|
base = super(ExpiringTokenAuthentication, self)
|
||||||
user, token = base.authenticate_credentials(key)
|
user, token = base.authenticate_credentials(key)
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,9 @@
|
||||||
"""Defines the permission classes used in the API.
|
"""Defines the permission classes used in the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from rest_framework import permissions, exceptions
|
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
|
from rest_framework import exceptions, permissions
|
||||||
|
|
||||||
from . import acl
|
from . import acl
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
|
||||||
# The namespace used for the API. It must match the namespace used in the
|
# The namespace used for the API. It must match the namespace used in the
|
||||||
# urlpatterns to include the API URLs.
|
# urlpatterns to include the API URLs.
|
||||||
API_NAMESPACE = "api"
|
API_NAMESPACE = "api"
|
||||||
|
@ -59,7 +58,3 @@ class NamespacedHMSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
|
||||||
serializer_related_field = NamespacedHRField
|
serializer_related_field = NamespacedHRField
|
||||||
serializer_url_field = NamespacedHIField
|
serializer_url_field = NamespacedHIField
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,12 +28,13 @@ can also be register. That way a complete API root page presenting all URLs
|
||||||
can be generated automatically.
|
can be generated automatically.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.urls import path, include
|
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.urls import include, path
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
from .routers import AllViewsRouter
|
from .routers import AllViewsRouter
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
app_name = "api"
|
app_name = "api"
|
||||||
|
|
||||||
|
|
|
@ -29,9 +29,9 @@ the response (JSON or other), the CSRF exempting, ...
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.models import Q
|
|
||||||
from django.contrib.auth.models import Group
|
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.models import Token
|
||||||
from rest_framework.authtoken.views import ObtainAuthToken
|
from rest_framework.authtoken.views import ObtainAuthToken
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
@ -41,7 +41,6 @@ from .pagination import PageSizedPagination
|
||||||
from .permissions import ACLPermission
|
from .permissions import ACLPermission
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ObtainExpiringAuthToken(ObtainAuthToken):
|
class ObtainExpiringAuthToken(ObtainAuthToken):
|
||||||
"""Exposes a view to obtain a authentication token.
|
"""Exposes a view to obtain a authentication token.
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,8 @@ from __future__ import unicode_literals
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from reversion.admin import VersionAdmin
|
from reversion.admin import VersionAdmin
|
||||||
|
|
||||||
from .models import Facture, Article, Banque, Paiement, Cotisation, Vente
|
from .models import (Article, Banque, CostEstimate, Cotisation, CustomInvoice,
|
||||||
from .models import CustomInvoice, CostEstimate
|
Facture, Paiement, Vente)
|
||||||
|
|
||||||
|
|
||||||
class FactureAdmin(VersionAdmin):
|
class FactureAdmin(VersionAdmin):
|
||||||
|
|
|
@ -23,13 +23,13 @@ from rest_framework import serializers
|
||||||
|
|
||||||
import cotisations.models as cotisations
|
import cotisations.models as cotisations
|
||||||
import preferences.models as preferences
|
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
|
from users.api.serializers import UserSerializer
|
||||||
|
|
||||||
|
|
||||||
class FactureSerializer(NamespacedHMSerializer):
|
class FactureSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `cotisations.models.Facture` objects.
|
"""Serialize `cotisations.models.Facture` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = cotisations.Facture
|
model = cotisations.Facture
|
||||||
|
@ -54,8 +54,7 @@ class BaseInvoiceSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class VenteSerializer(NamespacedHMSerializer):
|
class VenteSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `cotisations.models.Vente` objects.
|
"""Serialize `cotisations.models.Vente` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = cotisations.Vente
|
model = cotisations.Vente
|
||||||
|
@ -74,17 +73,24 @@ class VenteSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class ArticleSerializer(NamespacedHMSerializer):
|
class ArticleSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `cotisations.models.Article` objects.
|
"""Serialize `cotisations.models.Article` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = cotisations.Article
|
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):
|
class BanqueSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `cotisations.models.Banque` objects.
|
"""Serialize `cotisations.models.Banque` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = cotisations.Banque
|
model = cotisations.Banque
|
||||||
|
@ -92,8 +98,7 @@ class BanqueSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class PaiementSerializer(NamespacedHMSerializer):
|
class PaiementSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `cotisations.models.Paiement` objects.
|
"""Serialize `cotisations.models.Paiement` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = cotisations.Paiement
|
model = cotisations.Paiement
|
||||||
|
@ -101,17 +106,23 @@ class PaiementSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class CotisationSerializer(NamespacedHMSerializer):
|
class CotisationSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `cotisations.models.Cotisation` objects.
|
"""Serialize `cotisations.models.Cotisation` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = cotisations.Cotisation
|
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):
|
class ReminderUsersSerializer(UserSerializer):
|
||||||
"""Serialize the data about a mailing member.
|
"""Serialize the data about a mailing member."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta(UserSerializer.Meta):
|
class Meta(UserSerializer.Meta):
|
||||||
fields = ("get_full_name", "get_mail")
|
fields = ("get_full_name", "get_mail")
|
||||||
|
|
|
@ -27,12 +27,11 @@ urls_viewset = [
|
||||||
(r"cotisations/article", views.ArticleViewSet, None),
|
(r"cotisations/article", views.ArticleViewSet, None),
|
||||||
(r"cotisations/banque", views.BanqueViewSet, None),
|
(r"cotisations/banque", views.BanqueViewSet, None),
|
||||||
(r"cotisations/paiement", views.PaiementViewSet, None),
|
(r"cotisations/paiement", views.PaiementViewSet, None),
|
||||||
(r"cotisations/cotisation", views.CotisationViewSet, None)
|
(r"cotisations/cotisation", views.CotisationViewSet, None),
|
||||||
]
|
]
|
||||||
|
|
||||||
urls_view = [
|
urls_view = [
|
||||||
(r"cotisations/reminder-get-users", views.ReminderView),
|
(r"cotisations/reminder-get-users", views.ReminderView),
|
||||||
|
|
||||||
# Deprecated
|
# Deprecated
|
||||||
(r"reminder/get-users", views.ReminderView),
|
(r"reminder/get-users", views.ReminderView),
|
||||||
]
|
]
|
||||||
|
|
|
@ -19,71 +19,65 @@
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 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 cotisations.models as cotisations
|
||||||
import preferences.models as preferences
|
import preferences.models as preferences
|
||||||
|
|
||||||
|
from . import serializers
|
||||||
|
|
||||||
|
|
||||||
class FactureViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = cotisations.Facture.objects.all()
|
||||||
serializer_class = serializers.FactureSerializer
|
serializer_class = serializers.FactureSerializer
|
||||||
|
|
||||||
|
|
||||||
class FactureViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = cotisations.BaseInvoice.objects.all()
|
||||||
serializer_class = serializers.BaseInvoiceSerializer
|
serializer_class = serializers.BaseInvoiceSerializer
|
||||||
|
|
||||||
|
|
||||||
class VenteViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = cotisations.Vente.objects.all()
|
||||||
serializer_class = serializers.VenteSerializer
|
serializer_class = serializers.VenteSerializer
|
||||||
|
|
||||||
|
|
||||||
class ArticleViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = cotisations.Article.objects.all()
|
||||||
serializer_class = serializers.ArticleSerializer
|
serializer_class = serializers.ArticleSerializer
|
||||||
|
|
||||||
|
|
||||||
class BanqueViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = cotisations.Banque.objects.all()
|
||||||
serializer_class = serializers.BanqueSerializer
|
serializer_class = serializers.BanqueSerializer
|
||||||
|
|
||||||
|
|
||||||
class PaiementViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = cotisations.Paiement.objects.all()
|
||||||
serializer_class = serializers.PaiementSerializer
|
serializer_class = serializers.PaiementSerializer
|
||||||
|
|
||||||
|
|
||||||
class CotisationViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = cotisations.Cotisation.objects.all()
|
||||||
serializer_class = serializers.CotisationSerializer
|
serializer_class = serializers.CotisationSerializer
|
||||||
|
|
||||||
|
|
||||||
class ReminderView(generics.ListAPIView):
|
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()
|
queryset = preferences.Reminder.objects.all()
|
||||||
serializer_class = serializers.ReminderSerializer
|
serializer_class = serializers.ReminderSerializer
|
||||||
|
|
|
@ -8,4 +8,4 @@ from django.apps import AppConfig
|
||||||
class CotisationsConfig(AppConfig):
|
class CotisationsConfig(AppConfig):
|
||||||
"""Configuration of cotisations app."""
|
"""Configuration of cotisations app."""
|
||||||
|
|
||||||
name = "cotisations"
|
name = "cotisations"
|
||||||
|
|
|
@ -37,25 +37,18 @@ of each of the method.
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.db.models import Q
|
|
||||||
from django.forms import ModelForm, Form
|
|
||||||
from django.core.validators import MinValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
|
from django.db.models import Q
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.forms import Form, ModelForm
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from re2o.field_permissions import FieldPermissionFormMixin
|
from re2o.field_permissions import FieldPermissionFormMixin
|
||||||
from re2o.mixins import FormRevMixin
|
from re2o.mixins import FormRevMixin
|
||||||
from re2o.widgets import AutocompleteModelWidget
|
from re2o.widgets import AutocompleteModelWidget
|
||||||
from .models import (
|
|
||||||
Article,
|
from .models import (Article, Banque, CostEstimate, CustomInvoice, Facture,
|
||||||
Paiement,
|
Paiement, Vente)
|
||||||
Facture,
|
|
||||||
Banque,
|
|
||||||
CustomInvoice,
|
|
||||||
Vente,
|
|
||||||
CostEstimate,
|
|
||||||
)
|
|
||||||
from .payment_methods import balance
|
from .payment_methods import balance
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -32,29 +32,29 @@ each.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
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 import models
|
||||||
from django.db.models import Q, Max
|
from django.db.models import Max, 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.dispatch import receiver
|
||||||
from django.forms import ValidationError
|
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 import timezone
|
||||||
from django.utils.translation import ugettext_lazy as _
|
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 machines.models import regen
|
||||||
|
from preferences.models import CotisationsOption
|
||||||
from re2o.field_permissions import FieldPermissionModelMixin
|
from re2o.field_permissions import FieldPermissionModelMixin
|
||||||
from re2o.mixins import AclMixin, RevMixin
|
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):
|
class BaseInvoice(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
|
@ -360,7 +360,13 @@ def facture_post_save(**kwargs):
|
||||||
if facture.valid:
|
if facture.valid:
|
||||||
user = facture.user
|
user = facture.user
|
||||||
user.set_active()
|
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)
|
@receiver(post_delete, sender=Facture)
|
||||||
|
@ -369,7 +375,13 @@ def facture_post_delete(**kwargs):
|
||||||
Synchronise the LDAP user after an invoice has been deleted.
|
Synchronise the LDAP user after an invoice has been deleted.
|
||||||
"""
|
"""
|
||||||
user = kwargs["instance"].user
|
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):
|
class CustomInvoice(BaseInvoice):
|
||||||
|
@ -481,9 +493,7 @@ class Vente(RevMixin, AclMixin, models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (("change_all_vente", _("Can edit all the previous purchases")),)
|
||||||
("change_all_vente", _("Can edit all the previous purchases")),
|
|
||||||
)
|
|
||||||
verbose_name = _("purchase")
|
verbose_name = _("purchase")
|
||||||
verbose_name_plural = _("purchases")
|
verbose_name_plural = _("purchases")
|
||||||
|
|
||||||
|
@ -660,7 +670,13 @@ def vente_post_save(**kwargs):
|
||||||
purchase.cotisation.save()
|
purchase.cotisation.save()
|
||||||
user = purchase.facture.facture.user
|
user = purchase.facture.facture.user
|
||||||
user.set_active()
|
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
|
# TODO : change vente to purchase
|
||||||
|
@ -676,7 +692,13 @@ def vente_post_delete(**kwargs):
|
||||||
return
|
return
|
||||||
if purchase.type_cotisation:
|
if purchase.type_cotisation:
|
||||||
user = invoice.user
|
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):
|
class Article(RevMixin, AclMixin, models.Model):
|
||||||
|
@ -740,9 +762,7 @@ class Article(RevMixin, AclMixin, models.Model):
|
||||||
unique_together = ("name", "type_user")
|
unique_together = ("name", "type_user")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (("buy_every_article", _("Can buy every article")),)
|
||||||
("buy_every_article", _("Can buy every article")),
|
|
||||||
)
|
|
||||||
verbose_name = "article"
|
verbose_name = "article"
|
||||||
verbose_name_plural = "articles"
|
verbose_name_plural = "articles"
|
||||||
|
|
||||||
|
@ -844,9 +864,7 @@ class Paiement(RevMixin, AclMixin, models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (("use_every_payment", _("Can use every payment method")),)
|
||||||
("use_every_payment", _("Can use every payment method")),
|
|
||||||
)
|
|
||||||
verbose_name = _("payment method")
|
verbose_name = _("payment method")
|
||||||
verbose_name_plural = _("payment methods")
|
verbose_name_plural = _("payment methods")
|
||||||
|
|
||||||
|
|
|
@ -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]
|
PAYMENT_METHODS = [comnpay, cheque, balance, note_kfet, free]
|
||||||
|
|
|
@ -18,12 +18,11 @@
|
||||||
# You should have received a copy of the GNU General Public License along
|
# 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.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
from django.contrib import messages
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.contrib import messages
|
|
||||||
|
|
||||||
|
|
||||||
from cotisations.models import Paiement
|
from cotisations.models import Paiement
|
||||||
from cotisations.payment_methods.mixins import PaymentMethodMixin
|
from cotisations.payment_methods.mixins import PaymentMethodMixin
|
||||||
|
|
|
@ -20,8 +20,8 @@
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from re2o.mixins import FormRevMixin
|
|
||||||
from cotisations.models import Facture as Invoice
|
from cotisations.models import Facture as Invoice
|
||||||
|
from re2o.mixins import FormRevMixin
|
||||||
|
|
||||||
|
|
||||||
class InvoiceForm(FormRevMixin, forms.ModelForm):
|
class InvoiceForm(FormRevMixin, forms.ModelForm):
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
urlpatterns = [url(r"^validate/(?P<invoice_pk>[0-9]+)$", views.cheque, name="validate")]
|
urlpatterns = [url(r"^validate/(?P<invoice_pk>[0-9]+)$", views.cheque, name="validate")]
|
||||||
|
|
|
@ -23,17 +23,17 @@
|
||||||
Here are defined some views dedicated to cheque payement.
|
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 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 django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from cotisations.models import Facture as Invoice
|
from cotisations.models import Facture as Invoice
|
||||||
from cotisations.utils import find_payment_method
|
from cotisations.utils import find_payment_method
|
||||||
|
|
||||||
from .models import ChequePayment
|
|
||||||
from .forms import InvoiceForm
|
from .forms import InvoiceForm
|
||||||
|
from .models import ChequePayment
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
|
|
@ -3,15 +3,15 @@ The module in charge of handling the negociation with Comnpay
|
||||||
for online payment
|
for online payment
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import time
|
|
||||||
from random import randrange
|
|
||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import time
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from random import randrange
|
||||||
|
|
||||||
|
|
||||||
class Transaction:
|
class Transaction:
|
||||||
""" The class representing a transaction with all the functions
|
"""The class representing a transaction with all the functions
|
||||||
used during the negociation
|
used during the negociation
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ class Transaction:
|
||||||
self.idTransaction = ""
|
self.idTransaction = ""
|
||||||
|
|
||||||
def buildSecretHTML(self, produit="Produit", montant="0.00", 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
|
transaction
|
||||||
"""
|
"""
|
||||||
if idTransaction == "":
|
if idTransaction == "":
|
||||||
|
|
|
@ -25,8 +25,8 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from cotisations.models import Paiement
|
from cotisations.models import Paiement
|
||||||
from cotisations.payment_methods.mixins import PaymentMethodMixin
|
from cotisations.payment_methods.mixins import PaymentMethodMixin
|
||||||
|
|
||||||
from re2o.aes_field import AESEncryptedField
|
from re2o.aes_field import AESEncryptedField
|
||||||
|
|
||||||
from .comnpay import Transaction
|
from .comnpay import Transaction
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,8 +53,7 @@ class ComnpayPayment(PaymentMethodMixin, models.Model):
|
||||||
minimum_payment = models.DecimalField(
|
minimum_payment = models.DecimalField(
|
||||||
verbose_name=_("minimum payment"),
|
verbose_name=_("minimum payment"),
|
||||||
help_text=_(
|
help_text=_(
|
||||||
"The minimal amount of money you have to use when paying with"
|
"The minimal amount of money you have to use when paying with" " ComNpay."
|
||||||
" ComNpay."
|
|
||||||
),
|
),
|
||||||
max_digits=5,
|
max_digits=5,
|
||||||
decimal_places=2,
|
decimal_places=2,
|
||||||
|
@ -107,8 +106,7 @@ class ComnpayPayment(PaymentMethodMixin, models.Model):
|
||||||
return render(request, "cotisations/payment.html", r)
|
return render(request, "cotisations/payment.html", r)
|
||||||
|
|
||||||
def check_price(self, price, *args, **kwargs):
|
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 (
|
return (
|
||||||
(price >= self.minimum_payment),
|
(price >= self.minimum_payment),
|
||||||
_(
|
_(
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|
|
@ -25,16 +25,17 @@ Here are the views needed by comnpay
|
||||||
|
|
||||||
from collections import OrderedDict
|
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.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.datastructures import MultiValueDictKeyError
|
||||||
from django.utils.translation import ugettext as _
|
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.models import Facture
|
||||||
|
|
||||||
from .comnpay import Transaction
|
from .comnpay import Transaction
|
||||||
from .models import ComnpayPayment
|
from .models import ComnpayPayment
|
||||||
|
|
||||||
|
@ -55,7 +56,10 @@ def accept_payment(request, factureid):
|
||||||
)
|
)
|
||||||
# In case a cotisation was bought, inform the user, the
|
# In case a cotisation was bought, inform the user, the
|
||||||
# cotisation time has been extended too
|
# 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(
|
messages.success(
|
||||||
request,
|
request,
|
||||||
_(
|
_(
|
||||||
|
|
|
@ -21,9 +21,10 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from . import PAYMENT_METHODS
|
|
||||||
from cotisations.utils import find_payment_method
|
from cotisations.utils import find_payment_method
|
||||||
|
|
||||||
|
from . import PAYMENT_METHODS
|
||||||
|
|
||||||
|
|
||||||
def payment_method_factory(payment, *args, creation=True, **kwargs):
|
def payment_method_factory(payment, *args, creation=True, **kwargs):
|
||||||
"""This function finds the right payment method form for a given payment.
|
"""This function finds the right payment method form for a given payment.
|
||||||
|
|
|
@ -18,10 +18,9 @@
|
||||||
# You should have received a copy of the GNU General Public License along
|
# 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.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
from django.contrib import messages
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.contrib import messages
|
|
||||||
|
|
||||||
|
|
||||||
from cotisations.models import Paiement
|
from cotisations.models import Paiement
|
||||||
from cotisations.payment_methods.mixins import PaymentMethodMixin
|
from cotisations.payment_methods.mixins import PaymentMethodMixin
|
||||||
|
@ -43,8 +42,7 @@ class FreePayment(PaymentMethodMixin, models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
def end_payment(self, invoice, request):
|
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)
|
return invoice.paiement.end_payment(invoice, request, use_payment_method=False)
|
||||||
|
|
||||||
def check_price(self, price, user, *args, **kwargs):
|
def check_price(self, price, user, *args, **kwargs):
|
||||||
|
|
|
@ -19,17 +19,15 @@
|
||||||
# You should have received a copy of the GNU General Public License along
|
# 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.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
from django.contrib import messages
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.shortcuts import render
|
from django.shortcuts import redirect, render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.contrib import messages
|
|
||||||
|
|
||||||
from cotisations.models import Paiement
|
from cotisations.models import Paiement
|
||||||
from cotisations.payment_methods.mixins import PaymentMethodMixin
|
from cotisations.payment_methods.mixins import PaymentMethodMixin
|
||||||
|
|
||||||
from django.shortcuts import render, redirect
|
|
||||||
|
|
||||||
|
|
||||||
class NotePayment(PaymentMethodMixin, models.Model):
|
class NotePayment(PaymentMethodMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
|
|
||||||
""" Module pour dialoguer avec la NoteKfet2015 """
|
""" Module pour dialoguer avec la NoteKfet2015 """
|
||||||
|
|
||||||
import socket
|
|
||||||
import json
|
import json
|
||||||
|
import socket
|
||||||
import ssl
|
import ssl
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|
|
@ -26,22 +26,23 @@ Here are the views needed by comnpay
|
||||||
|
|
||||||
from collections import OrderedDict
|
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.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.datastructures import MultiValueDictKeyError
|
||||||
from django.utils.translation import ugettext as _
|
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.models import Facture
|
||||||
from cotisations.utils import find_payment_method
|
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 re2o.acl import can_create, can_edit
|
||||||
from .note import login, don
|
from re2o.views import form
|
||||||
|
|
||||||
from .forms import NoteCredentialForm
|
from .forms import NoteCredentialForm
|
||||||
|
from .models import NotePayment
|
||||||
|
from .note import don, login
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
|
|
@ -19,10 +19,11 @@
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
from django.conf.urls import include, url
|
from django.conf.urls import include, url
|
||||||
from . import comnpay, cheque, note_kfet
|
|
||||||
|
from . import cheque, comnpay, note_kfet
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r"^comnpay/", include((comnpay.urls, 'comnpay'), namespace="comnpay")),
|
url(r"^comnpay/", include((comnpay.urls, "comnpay"), namespace="comnpay")),
|
||||||
url(r"^cheque/", include((cheque.urls, 'cheque'), namespace="cheque")),
|
url(r"^cheque/", include((cheque.urls, "cheque"), namespace="cheque")),
|
||||||
url(r"^note_kfet/", include((note_kfet.urls, 'note_kfet'), namespace="note_kfet")),
|
url(r"^note_kfet/", include((note_kfet.urls, "note_kfet"), namespace="note_kfet")),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
from django.utils import timezone
|
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
from users.models import User
|
from users.models import User
|
||||||
from .models import Vente, Facture, Cotisation, Paiement
|
|
||||||
|
from .models import Cotisation, Facture, Paiement, Vente
|
||||||
|
|
||||||
|
|
||||||
class VenteModelTests(TestCase):
|
class VenteModelTests(TestCase):
|
||||||
|
@ -74,7 +75,7 @@ class VenteModelTests(TestCase):
|
||||||
def test_one_month_and_one_week_cotisation(self):
|
def test_one_month_and_one_week_cotisation(self):
|
||||||
"""
|
"""
|
||||||
It should be possible to have one day membership.
|
It should be possible to have one day membership.
|
||||||
Add one mounth and one week of membership and one mounth
|
Add one mounth and one week of membership and one mounth
|
||||||
and one week of connection
|
and one week of connection
|
||||||
"""
|
"""
|
||||||
date = timezone.now()
|
date = timezone.now()
|
||||||
|
@ -111,12 +112,21 @@ class VenteModelTests(TestCase):
|
||||||
duration_days_connection=1,
|
duration_days_connection=1,
|
||||||
duration_membership=0,
|
duration_membership=0,
|
||||||
duration_deys_membership=1,
|
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()
|
v.save()
|
||||||
self.assertEqual(v.cotisation.date_end_con, timezone.make_aware(datetime.datetime(1998, 10, 17)))
|
self.assertEqual(
|
||||||
self.assertEqual(v.cotisation.date_end_memb, timezone.make_aware(datetime.datetime(1998, 10, 17)))
|
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):
|
def test_one_day_cotisation_membership_only(self):
|
||||||
"""
|
"""
|
||||||
|
@ -207,12 +217,21 @@ class VenteModelTests(TestCase):
|
||||||
duration_days_connection=0,
|
duration_days_connection=0,
|
||||||
duration_membership=0,
|
duration_membership=0,
|
||||||
duration_days_membership=1,
|
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()
|
v.save()
|
||||||
self.assertEqual(v.cotisation.date_end_con, timezone.make_aware(datetime.datetime(1998, 10, 17)))
|
self.assertEqual(
|
||||||
self.assertEqual(v.cotisation.date_end_memb, timezone.make_aware(datetime.datetime(1998, 10, 16)))
|
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):
|
def test_cotisation_membership_diff_connection(self):
|
||||||
"""
|
"""
|
||||||
|
@ -252,9 +271,11 @@ class FactureModelTests(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.user = User.objects.create(pseudo="testUserPlop", email="test@example.org")
|
self.user = User.objects.create(pseudo="testUserPlop", email="test@example.org")
|
||||||
self.paiement = Paiement.objects.create(moyen="test payment")
|
self.paiement = Paiement.objects.create(moyen="test payment")
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.user.delete()
|
self.user.delete()
|
||||||
self.paiement.delete()
|
self.paiement.delete()
|
||||||
|
|
||||||
def test_cotisations_prolongation(self):
|
def test_cotisations_prolongation(self):
|
||||||
"""When user already have one valid cotisation, the new one should be
|
"""When user already have one valid cotisation, the new one should be
|
||||||
added at the end of the existing one."""
|
added at the end of the existing one."""
|
||||||
|
@ -300,4 +321,3 @@ class FactureModelTests(TestCase):
|
||||||
raise e
|
raise e
|
||||||
invoice1.delete()
|
invoice1.delete()
|
||||||
invoice2.delete()
|
invoice2.delete()
|
||||||
|
|
||||||
|
|
|
@ -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.test import TestCase
|
||||||
from django.urls import reverse
|
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 django.utils import timezone
|
||||||
|
|
||||||
from users.models import Adherent
|
from users.models import Adherent
|
||||||
from .models import Vente, Facture, Cotisation, Paiement, Article
|
|
||||||
|
from .models import Article, Cotisation, Facture, Paiement, Vente
|
||||||
|
|
||||||
|
|
||||||
class NewFactureTests(TestCase):
|
class NewFactureTests(TestCase):
|
||||||
|
|
|
@ -26,20 +26,19 @@ Used to generated PDF invoice.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
import tempfile
|
|
||||||
from subprocess import Popen, PIPE
|
|
||||||
import os
|
import os
|
||||||
|
import tempfile
|
||||||
from datetime import datetime
|
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.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 django.utils.text import slugify
|
||||||
|
|
||||||
from re2o.mixins import AclMixin, RevMixin
|
|
||||||
from preferences.models import CotisationsOption
|
from preferences.models import CotisationsOption
|
||||||
|
from re2o.mixins import AclMixin, RevMixin
|
||||||
|
|
||||||
TEMP_PREFIX = getattr(settings, "TEX_TEMP_PREFIX", "render_tex-")
|
TEMP_PREFIX = getattr(settings, "TEX_TEMP_PREFIX", "render_tex-")
|
||||||
CACHE_PREFIX = getattr(settings, "TEX_CACHE_PREFIX", "render-tex")
|
CACHE_PREFIX = getattr(settings, "TEX_CACHE_PREFIX", "render-tex")
|
||||||
|
|
|
@ -27,23 +27,18 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from . import views, views_autocomplete
|
from . import payment_methods, views, views_autocomplete
|
||||||
from . import payment_methods
|
|
||||||
|
|
||||||
app_name ="cotisations"
|
app_name = "cotisations"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("new_facture/<int:userid>", views.new_facture, name="new-facture"),
|
path("new_facture/<int:userid>", views.new_facture, name="new-facture"),
|
||||||
path(
|
path("edit_facture/<int:factureid>", views.edit_facture, name="edit-facture"),
|
||||||
"edit_facture/<int:factureid>", views.edit_facture, name="edit-facture"
|
|
||||||
),
|
|
||||||
path("del_facture/<int:factureid>", views.del_facture, name="del-facture"),
|
path("del_facture/<int:factureid>", views.del_facture, name="del-facture"),
|
||||||
path("facture_pdf/<int:factureid>", views.facture_pdf, name="facture-pdf"),
|
path("facture_pdf/<int:factureid>", views.facture_pdf, name="facture-pdf"),
|
||||||
path("voucher_pdf/<int:factureid>", views.voucher_pdf, name="voucher-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("new_cost_estimate", views.new_cost_estimate, name="new-cost-estimate"),
|
||||||
path(
|
path("index_cost_estimate", views.index_cost_estimate, name="index-cost-estimate"),
|
||||||
"index_cost_estimate", views.index_cost_estimate, name="index-cost-estimate"
|
|
||||||
),
|
|
||||||
path(
|
path(
|
||||||
"cost_estimate_pdf/<int:costestimateid>",
|
"cost_estimate_pdf/<int:costestimateid>",
|
||||||
views.cost_estimate_pdf,
|
views.cost_estimate_pdf,
|
||||||
|
@ -87,9 +82,7 @@ urlpatterns = [
|
||||||
),
|
),
|
||||||
path("credit_solde/<int:userid>", views.credit_solde, name="credit-solde"),
|
path("credit_solde/<int:userid>", views.credit_solde, name="credit-solde"),
|
||||||
path("add_article", views.add_article, name="add-article"),
|
path("add_article", views.add_article, name="add-article"),
|
||||||
path(
|
path("edit_article/<int:articleid>", views.edit_article, name="edit-article"),
|
||||||
"edit_article/<int:articleid>", views.edit_article, name="edit-article"
|
|
||||||
),
|
|
||||||
path("del_article", views.del_article, name="del-article"),
|
path("del_article", views.del_article, name="del-article"),
|
||||||
path("add_paiement", views.add_paiement, name="add-paiement"),
|
path("add_paiement", views.add_paiement, name="add-paiement"),
|
||||||
path(
|
path(
|
||||||
|
@ -107,5 +100,9 @@ urlpatterns = [
|
||||||
path("control", views.control, name="control"),
|
path("control", views.control, name="control"),
|
||||||
path("", views.index, name="index"),
|
path("", views.index, name="index"),
|
||||||
### Autocomplete Views
|
### 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
|
] + payment_methods.urls.urlpatterns
|
||||||
|
|
|
@ -21,14 +21,16 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from django.template.loader import get_template
|
|
||||||
from django.core.mail import EmailMessage
|
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.mail_utils import send_mail_object
|
||||||
|
from re2o.settings import LOGO_PATH
|
||||||
|
|
||||||
from .tex import create_pdf
|
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):
|
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),
|
"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)
|
pdf = create_pdf(template, ctx)
|
||||||
template = get_template("cotisations/email_invoice")
|
template = get_template("cotisations/email_invoice")
|
||||||
|
|
||||||
|
@ -106,7 +110,9 @@ def send_mail_voucher(invoice, request=None):
|
||||||
"email": invoice.user.email,
|
"email": invoice.user.email,
|
||||||
"phone": invoice.user.telephone,
|
"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.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(
|
templatename = CotisationsOption.get_cached_value(
|
||||||
"voucher_template"
|
"voucher_template"
|
||||||
|
|
|
@ -29,62 +29,38 @@ The different views used in the Cotisations module
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import os
|
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.contrib import messages
|
||||||
from django.db.models import ProtectedError
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.db.models import Q
|
from django.db.models import ProtectedError, Q
|
||||||
from django.forms import modelformset_factory, formset_factory
|
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 import timezone
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
# Import des models, forms et fonctions re2o
|
# Import des models, forms et fonctions re2o
|
||||||
from reversion import revisions as reversion
|
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 preferences.models import AssoOption, GeneralOption, Mandate
|
||||||
from .models import (
|
from re2o import settings
|
||||||
Facture,
|
from re2o.acl import (can_change, can_create, can_delete, can_delete_set,
|
||||||
Article,
|
can_edit, can_view, can_view_all)
|
||||||
Vente,
|
from re2o.base import SortTable, re2o_paginator
|
||||||
Paiement,
|
from re2o.settings import LOGO_PATH
|
||||||
Banque,
|
from re2o.views import form
|
||||||
CustomInvoice,
|
from users.models import User
|
||||||
BaseInvoice,
|
|
||||||
CostEstimate,
|
from .forms import (ArticleForm, BanqueForm, CostEstimateForm,
|
||||||
)
|
CustomInvoiceForm, DelArticleForm, DelBanqueForm,
|
||||||
from .forms import (
|
DelPaiementForm, DiscountForm, FactureForm, PaiementForm,
|
||||||
FactureForm,
|
RechargeForm, SelectArticleForm)
|
||||||
ArticleForm,
|
from .models import (Article, Banque, BaseInvoice, CostEstimate, CustomInvoice,
|
||||||
DelArticleForm,
|
Facture, Paiement, Vente)
|
||||||
PaiementForm,
|
|
||||||
DelPaiementForm,
|
|
||||||
BanqueForm,
|
|
||||||
DelBanqueForm,
|
|
||||||
SelectArticleForm,
|
|
||||||
RechargeForm,
|
|
||||||
CustomInvoiceForm,
|
|
||||||
DiscountForm,
|
|
||||||
CostEstimateForm,
|
|
||||||
)
|
|
||||||
from .tex import render_invoice, render_voucher, escape_chars
|
|
||||||
from .payment_methods.forms import payment_method_factory
|
from .payment_methods.forms import payment_method_factory
|
||||||
|
from .tex import escape_chars, render_invoice, render_voucher
|
||||||
from .utils import find_payment_method
|
from .utils import find_payment_method
|
||||||
|
|
||||||
|
|
||||||
|
@ -136,7 +112,7 @@ def new_facture(request, user, userid):
|
||||||
duration_membership=article.duration_membership,
|
duration_membership=article.duration_membership,
|
||||||
duration_days_membership=article.duration_days_membership,
|
duration_days_membership=article.duration_days_membership,
|
||||||
number=quantity,
|
number=quantity,
|
||||||
)
|
)
|
||||||
purchases.append(new_purchase)
|
purchases.append(new_purchase)
|
||||||
p = find_payment_method(new_invoice_instance.paiement)
|
p = find_payment_method(new_invoice_instance.paiement)
|
||||||
if hasattr(p, "check_price"):
|
if hasattr(p, "check_price"):
|
||||||
|
@ -1057,12 +1033,15 @@ def voucher_pdf(request, invoice, **_kwargs):
|
||||||
"lastname": invoice.user.surname,
|
"lastname": invoice.user.surname,
|
||||||
"email": invoice.user.email,
|
"email": invoice.user.email,
|
||||||
"phone": invoice.user.telephone,
|
"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,
|
"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."""
|
"""View used to display the cotisations on a user's profil."""
|
||||||
|
|
||||||
factures = Facture.objects.filter(user=user)
|
factures = Facture.objects.filter(user=user)
|
||||||
|
@ -1071,16 +1050,16 @@ def aff_profil(request,user):
|
||||||
request.GET.get("col"),
|
request.GET.get("col"),
|
||||||
request.GET.get("order"),
|
request.GET.get("order"),
|
||||||
SortTable.COTISATIONS_INDEX,
|
SortTable.COTISATIONS_INDEX,
|
||||||
)
|
)
|
||||||
|
|
||||||
pagination_large_number = GeneralOption.get_cached_value("pagination_large_number")
|
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 = {
|
context = {
|
||||||
"users":user,
|
"users": user,
|
||||||
"facture_list": factures,
|
"facture_list": factures,
|
||||||
}
|
}
|
||||||
|
|
||||||
return render_to_string(
|
return render_to_string(
|
||||||
"cotisations/aff_profil.html",context=context,request=request,using=None
|
"cotisations/aff_profil.html", context=context, request=request, using=None
|
||||||
)
|
)
|
||||||
|
|
|
@ -31,20 +31,13 @@ Here are defined the autocomplete class based view.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db.models import Q, Value, CharField
|
from django.db.models import CharField, Q, Value
|
||||||
|
|
||||||
from .models import (
|
|
||||||
Banque
|
|
||||||
)
|
|
||||||
|
|
||||||
|
from re2o.acl import can_view_all
|
||||||
from re2o.views import AutocompleteViewMixin
|
from re2o.views import AutocompleteViewMixin
|
||||||
|
|
||||||
from re2o.acl import (
|
from .models import Banque
|
||||||
can_view_all,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class BanqueAutocomplete(AutocompleteViewMixin):
|
class BanqueAutocomplete(AutocompleteViewMixin):
|
||||||
obj_type = Banque
|
obj_type = Banque
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -34,12 +34,12 @@ https://github.com/FreeRADIUS/freeradius-server/blob/master/src/modules/rlm_pyth
|
||||||
Inspired by Daniel Stan in Crans
|
Inspired by Daniel Stan in Crans
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import logging
|
|
||||||
import traceback
|
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.core.wsgi import get_wsgi_application
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
|
||||||
|
@ -54,11 +54,10 @@ os.chdir(proj_path)
|
||||||
# This is so models get loaded.
|
# This is so models get loaded.
|
||||||
application = get_wsgi_application()
|
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 topologie.models import Port, Switch
|
||||||
from users.models import User
|
from users.models import User
|
||||||
from preferences.models import RadiusOption
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
|
@ -76,7 +75,7 @@ class RadiusdHandler(logging.Handler):
|
||||||
radiusd.radlog(rad_sig, str(record.msg))
|
radiusd.radlog(rad_sig, str(record.msg))
|
||||||
|
|
||||||
|
|
||||||
# Init for logging
|
# Init for logging
|
||||||
logger = logging.getLogger("auth.py")
|
logger = logging.getLogger("auth.py")
|
||||||
logger.setLevel(logging.DEBUG)
|
logger.setLevel(logging.DEBUG)
|
||||||
formatter = logging.Formatter("%(name)s: [%(levelname)s] %(message)s")
|
formatter = logging.Formatter("%(name)s: [%(levelname)s] %(message)s")
|
||||||
|
@ -132,10 +131,10 @@ def authorize(data):
|
||||||
- If the nas is known, we apply the 802.1X if enabled,
|
- If the nas is known, we apply the 802.1X if enabled,
|
||||||
- It the nas is known AND nas auth is enabled with mac address, returns
|
- It the nas is known AND nas auth is enabled with mac address, returns
|
||||||
accept here"""
|
accept here"""
|
||||||
# For proxified request, split
|
# For proxified request, split
|
||||||
nas = data.get("NAS-IP-Address", data.get("NAS-Identifier", None))
|
nas = data.get("NAS-IP-Address", data.get("NAS-Identifier", None))
|
||||||
nas_instance = find_nas_from_request(nas)
|
nas_instance = find_nas_from_request(nas)
|
||||||
# For none proxified requests
|
# For none proxified requests
|
||||||
nas_type = None
|
nas_type = None
|
||||||
if nas_instance:
|
if nas_instance:
|
||||||
nas_type = Nas.objects.filter(nas_type=nas_instance.machine_type).first()
|
nas_type = Nas.objects.filter(nas_type=nas_instance.machine_type).first()
|
||||||
|
@ -162,12 +161,11 @@ def authorize(data):
|
||||||
|
|
||||||
@radius_event
|
@radius_event
|
||||||
def post_auth(data):
|
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 = data.get("NAS-IP-Address", data.get("NAS-Identifier", None))
|
||||||
nas_instance = find_nas_from_request(nas)
|
nas_instance = find_nas_from_request(nas)
|
||||||
# All non proxified requests
|
# All non proxified requests
|
||||||
if not nas_instance:
|
if not nas_instance:
|
||||||
logger.info("Proxified request, nas unknown")
|
logger.info("Proxified request, nas unknown")
|
||||||
return radiusd.RLM_MODULE_OK
|
return radiusd.RLM_MODULE_OK
|
||||||
|
@ -309,7 +307,7 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address):
|
||||||
- no room : Decision set in Re2o RadiusOption,
|
- no room : Decision set in Re2o RadiusOption,
|
||||||
- no user in this room : Reject,
|
- no user in this room : Reject,
|
||||||
- user of this room is banned or disable : Reject,
|
- user of this room is banned or disable : Reject,
|
||||||
- user of this room non-contributor and not whitelisted:
|
- user of this room non-contributor and not whitelisted:
|
||||||
Decision set in Re2o RadiusOption
|
Decision set in Re2o RadiusOption
|
||||||
- mode common :
|
- mode common :
|
||||||
- mac-address already registered:
|
- mac-address already registered:
|
||||||
|
@ -336,7 +334,7 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address):
|
||||||
}
|
}
|
||||||
# Get port from switch and port number
|
# Get port from switch and port number
|
||||||
extra_log = ""
|
extra_log = ""
|
||||||
# If NAS is unknown, go to default vlan
|
# If NAS is unknown, go to default vlan
|
||||||
if not nas_machine:
|
if not nas_machine:
|
||||||
return (
|
return (
|
||||||
"?",
|
"?",
|
||||||
|
@ -366,7 +364,7 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address):
|
||||||
RadiusOption.get_cached_value("unknown_port") != RadiusOption.REJECT,
|
RadiusOption.get_cached_value("unknown_port") != RadiusOption.REJECT,
|
||||||
RadiusOption.get_attributes("unknown_port_attributes", attributes_kwargs),
|
RadiusOption.get_attributes("unknown_port_attributes", attributes_kwargs),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Retrieve port profile
|
# Retrieve port profile
|
||||||
port_profile = port.get_port_profile
|
port_profile = port.get_port_profile
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import (
|
from .models import (LdapServiceUser, LdapServiceUserGroup, LdapUser,
|
||||||
LdapUser,
|
LdapUserGroup)
|
||||||
LdapServiceUser,
|
|
||||||
LdapServiceUserGroup,
|
|
||||||
LdapUserGroup,
|
|
||||||
)
|
|
||||||
|
|
||||||
class LdapUserAdmin(admin.ModelAdmin):
|
class LdapUserAdmin(admin.ModelAdmin):
|
||||||
"""LdapUser Admin view. Can't change password, manage
|
"""LdapUser Admin view. Can't change password, manage
|
||||||
|
@ -15,6 +12,7 @@ class LdapUserAdmin(admin.ModelAdmin):
|
||||||
Django ModelAdmin: Apply on django ModelAdmin
|
Django ModelAdmin: Apply on django ModelAdmin
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
list_display = ("name", "uidNumber", "login_shell")
|
list_display = ("name", "uidNumber", "login_shell")
|
||||||
exclude = ("user_password", "sambat_nt_password")
|
exclude = ("user_password", "sambat_nt_password")
|
||||||
search_fields = ("name",)
|
search_fields = ("name",)
|
||||||
|
|
|
@ -2,4 +2,4 @@ from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
class LdapSyncConfig(AppConfig):
|
class LdapSyncConfig(AppConfig):
|
||||||
name = 'ldap_sync'
|
name = "ldap_sync"
|
||||||
|
|
|
@ -18,11 +18,12 @@
|
||||||
import subprocess
|
import subprocess
|
||||||
from base64 import decodebytes
|
from base64 import decodebytes
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
|
||||||
from django.conf import settings
|
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_serviceuser, synchronise_user,
|
||||||
from ldap_sync.models import synchronise_user, synchronise_serviceuser, synchronise_usergroup
|
synchronise_usergroup)
|
||||||
|
from users.models import ListRight, User
|
||||||
|
|
||||||
|
|
||||||
def split_lines(lines):
|
def split_lines(lines):
|
||||||
|
|
|
@ -19,8 +19,8 @@
|
||||||
#
|
#
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
|
|
||||||
from users.models import User
|
|
||||||
from ldap_sync.models import synchronise_user
|
from ldap_sync.models import synchronise_user
|
||||||
|
from users.models import User
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
|
|
|
@ -1,18 +1,16 @@
|
||||||
import sys
|
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
|
||||||
import ldapdb.models.fields
|
import ldapdb.models.fields
|
||||||
|
from django.conf import settings
|
||||||
import users.signals
|
from django.contrib.auth.models import Group
|
||||||
import users.models
|
from django.db import models
|
||||||
|
from django.dispatch import receiver
|
||||||
|
|
||||||
import machines.models
|
import machines.models
|
||||||
|
import users.models
|
||||||
|
import users.signals
|
||||||
|
|
||||||
|
|
||||||
class LdapUser(ldapdb.models.Model):
|
class LdapUser(ldapdb.models.Model):
|
||||||
"""A class representing a LdapUser in LDAP, its LDAP conterpart.
|
"""A class representing a LdapUser in LDAP, its LDAP conterpart.
|
||||||
|
@ -110,13 +108,13 @@ def synchronise_user(sender, **kwargs):
|
||||||
* mac_refresh : Default `True`. When True, synchronise the list of mac addresses.
|
* mac_refresh : Default `True`. When True, synchronise the list of mac addresses.
|
||||||
* group_refresh: Default `False`. When `True` synchronise the groups of the instance.
|
* group_refresh: Default `False`. When `True` synchronise the groups of the instance.
|
||||||
"""
|
"""
|
||||||
base=kwargs.get('base', True)
|
base = kwargs.get("base", True)
|
||||||
access_refresh=kwargs.get('access_refresh', True)
|
access_refresh = kwargs.get("access_refresh", True)
|
||||||
mac_refresh=kwargs.get('mac_refresh', True )
|
mac_refresh = kwargs.get("mac_refresh", True)
|
||||||
group_refresh=kwargs.get('group_refresh', False)
|
group_refresh = kwargs.get("group_refresh", False)
|
||||||
|
|
||||||
|
user = kwargs["instance"]
|
||||||
|
|
||||||
user=kwargs["instance"]
|
|
||||||
|
|
||||||
if sys.version_info[0] >= 3 and (
|
if sys.version_info[0] >= 3 and (
|
||||||
user.state == user.STATE_ACTIVE
|
user.state == user.STATE_ACTIVE
|
||||||
or user.state == user.STATE_ARCHIVE
|
or user.state == user.STATE_ARCHIVE
|
||||||
|
@ -136,9 +134,7 @@ def synchronise_user(sender, **kwargs):
|
||||||
user_ldap.dialupAccess = str(user.has_access())
|
user_ldap.dialupAccess = str(user.has_access())
|
||||||
user_ldap.home_directory = user.home_directory
|
user_ldap.home_directory = user.home_directory
|
||||||
user_ldap.mail = user.get_mail
|
user_ldap.mail = user.get_mail
|
||||||
user_ldap.given_name = (
|
user_ldap.given_name = user.surname.lower() + "_" + user.name.lower()[:3]
|
||||||
user.surname.lower() + "_" + user.name.lower()[:3]
|
|
||||||
)
|
|
||||||
user_ldap.gid = settings.LDAP["user_gid"]
|
user_ldap.gid = settings.LDAP["user_gid"]
|
||||||
if "{SSHA}" in user.password or "{SMD5}" in user.password:
|
if "{SSHA}" in user.password or "{SMD5}" in user.password:
|
||||||
# We remove the extra $ added at import from ldap
|
# 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)
|
# be part of the updated group (case of group removal)
|
||||||
for group in Group.objects.all():
|
for group in Group.objects.all():
|
||||||
if hasattr(group, "listright"):
|
if hasattr(group, "listright"):
|
||||||
synchronise_usergroup(users.models.ListRight, instance=group.listright)
|
synchronise_usergroup(
|
||||||
|
users.models.ListRight, instance=group.listright
|
||||||
|
)
|
||||||
user_ldap.save()
|
user_ldap.save()
|
||||||
|
|
||||||
|
|
||||||
@receiver(users.signals.remove, sender=users.models.User)
|
@receiver(users.signals.remove, sender=users.models.User)
|
||||||
def remove_user(sender, **kwargs):
|
def remove_user(sender, **kwargs):
|
||||||
user = kwargs["instance"]
|
user = kwargs["instance"]
|
||||||
|
@ -181,6 +180,7 @@ def remove_user(sender, **kwargs):
|
||||||
except LdapUser.DoesNotExist:
|
except LdapUser.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@receiver(users.signals.remove_mass, sender=users.models.User)
|
@receiver(users.signals.remove_mass, sender=users.models.User)
|
||||||
def remove_users(sender, **kwargs):
|
def remove_users(sender, **kwargs):
|
||||||
queryset_users = kwargs["queryset"]
|
queryset_users = kwargs["queryset"]
|
||||||
|
@ -217,6 +217,7 @@ class LdapUserGroup(ldapdb.models.Model):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
@receiver(users.signals.synchronise, sender=users.models.ListRight)
|
@receiver(users.signals.synchronise, sender=users.models.ListRight)
|
||||||
def synchronise_usergroup(sender, **kwargs):
|
def synchronise_usergroup(sender, **kwargs):
|
||||||
group = kwargs["instance"]
|
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.members = [user.pseudo for user in group.user_set.all()]
|
||||||
group_ldap.save()
|
group_ldap.save()
|
||||||
|
|
||||||
|
|
||||||
@receiver(users.signals.remove, sender=users.models.ListRight)
|
@receiver(users.signals.remove, sender=users.models.ListRight)
|
||||||
def remove_usergroup(sender, **kwargs):
|
def remove_usergroup(sender, **kwargs):
|
||||||
group = kwargs["instance"]
|
group = kwargs["instance"]
|
||||||
|
@ -238,7 +240,6 @@ def remove_usergroup(sender, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class LdapServiceUser(ldapdb.models.Model):
|
class LdapServiceUser(ldapdb.models.Model):
|
||||||
"""A class representing a ServiceUser in LDAP, its LDAP conterpart.
|
"""A class representing a ServiceUser in LDAP, its LDAP conterpart.
|
||||||
Synced from ServiceUser, with a copy of its attributes/fields into LDAP,
|
Synced from ServiceUser, with a copy of its attributes/fields into LDAP,
|
||||||
|
@ -296,6 +297,7 @@ def synchronise_serviceuser(sender, **kwargs):
|
||||||
user_ldap.save()
|
user_ldap.save()
|
||||||
synchronise_serviceuser_group(user)
|
synchronise_serviceuser_group(user)
|
||||||
|
|
||||||
|
|
||||||
@receiver(users.signals.remove, sender=users.models.ServiceUser)
|
@receiver(users.signals.remove, sender=users.models.ServiceUser)
|
||||||
def remove_serviceuser(sender, **kwargs):
|
def remove_serviceuser(sender, **kwargs):
|
||||||
user = kwargs["instance"]
|
user = kwargs["instance"]
|
||||||
|
@ -331,4 +333,3 @@ class LdapServiceUserGroup(ldapdb.models.Model):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from .import views
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
app_name = "ldap_sync"
|
app_name = "ldap_sync"
|
||||||
|
|
||||||
|
|
|
@ -8,4 +8,4 @@ from django.apps import AppConfig
|
||||||
class LogsConfig(AppConfig):
|
class LogsConfig(AppConfig):
|
||||||
"""Configuration of logs app."""
|
"""Configuration of logs app."""
|
||||||
|
|
||||||
name = "logs"
|
name = "logs"
|
||||||
|
|
|
@ -21,13 +21,11 @@
|
||||||
|
|
||||||
"""The forms used by the machine search view"""
|
"""The forms used by the machine search view"""
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.forms import Form
|
from django.forms import Form
|
||||||
from django.utils.translation import ugettext_lazy as _
|
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 all models in which there are classes to be filtered on
|
||||||
import cotisations.models
|
import cotisations.models
|
||||||
|
@ -35,7 +33,8 @@ import machines.models
|
||||||
import preferences.models
|
import preferences.models
|
||||||
import topologie.models
|
import topologie.models
|
||||||
import users.models
|
import users.models
|
||||||
|
from re2o.base import get_input_formats_help_text
|
||||||
|
from re2o.widgets import AutocompleteModelWidget
|
||||||
|
|
||||||
CHOICES_ACTION_TYPE = (
|
CHOICES_ACTION_TYPE = (
|
||||||
("users", _("Users")),
|
("users", _("Users")),
|
||||||
|
|
|
@ -21,23 +21,17 @@
|
||||||
"""logs.models
|
"""logs.models
|
||||||
The models definitions for the logs app
|
The models definitions for the logs app
|
||||||
"""
|
"""
|
||||||
from reversion.models import Version, Revision
|
from django.apps import apps
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.apps import apps
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from netaddr import EUI
|
|
||||||
from macaddress.fields import default_dialect
|
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, IpList, Machine, MachineType
|
||||||
from machines.models import Interface
|
from topologie.models import Port, Room
|
||||||
from machines.models import Machine
|
from users.models import Adherent, Club, User
|
||||||
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 .forms import classes_for_action_type
|
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
|
# The lookup is done in a json string, so it has to be formated
|
||||||
# based on the value's type (to add " or not)
|
# based on the value's type (to add " or not)
|
||||||
if type(value) is str:
|
if type(value) is str:
|
||||||
formatted_value = "\"{}\"".format(value)
|
formatted_value = '"{}"'.format(value)
|
||||||
else:
|
else:
|
||||||
formatted_value = str(value)
|
formatted_value = str(value)
|
||||||
|
|
||||||
return (
|
return Q(serialized_data__contains='"{}": {},'.format(key, formatted_value)) | Q(
|
||||||
Q(serialized_data__contains='\"{}\": {},'.format(key, formatted_value))
|
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 #
|
# Machine history search #
|
||||||
############################
|
############################
|
||||||
|
|
||||||
|
|
||||||
class MachineHistorySearchEvent:
|
class MachineHistorySearchEvent:
|
||||||
def __init__(self, user, machine, interface, start=None, end=None):
|
def __init__(self, user, machine, interface, start=None, end=None):
|
||||||
"""Initialise an instance of MachineHistorySearchEvent.
|
"""Initialise an instance of MachineHistorySearchEvent.
|
||||||
|
@ -113,7 +107,7 @@ class MachineHistorySearchEvent:
|
||||||
self.ipv4,
|
self.ipv4,
|
||||||
self.start_date,
|
self.start_date,
|
||||||
self.end_date,
|
self.end_date,
|
||||||
self.comment or "No comment"
|
self.comment or "No comment",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -300,6 +294,7 @@ class MachineHistorySearch:
|
||||||
# Generic history classes #
|
# Generic history classes #
|
||||||
############################
|
############################
|
||||||
|
|
||||||
|
|
||||||
class RelatedHistory:
|
class RelatedHistory:
|
||||||
def __init__(self, version):
|
def __init__(self, version):
|
||||||
"""Initialise an instance of RelatedHistory.
|
"""Initialise an instance of RelatedHistory.
|
||||||
|
@ -317,10 +312,7 @@ class RelatedHistory:
|
||||||
self.name = "{}: {}".format(self.model_name.title(), self.name)
|
self.name = "{}: {}".format(self.model_name.title(), self.name)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return (
|
return self.model_name == other.model_name and self.object_id == other.object_id
|
||||||
self.model_name == other.model_name
|
|
||||||
and self.object_id == other.object_id
|
|
||||||
)
|
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash((self.model_name, self.object_id))
|
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
|
# Take into account keys that may exist in only one dict
|
||||||
if field in self.previous_version.field_dict:
|
if field in self.previous_version.field_dict:
|
||||||
old_value = self._repr(
|
old_value = self._repr(
|
||||||
field,
|
field, self.previous_version.field_dict[field]
|
||||||
self.previous_version.field_dict[field]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if field in self.version.field_dict:
|
if field in self.version.field_dict:
|
||||||
new_value = self._repr(
|
new_value = self._repr(field, self.version.field_dict[field])
|
||||||
field,
|
|
||||||
self.version.field_dict[field]
|
|
||||||
)
|
|
||||||
|
|
||||||
edits.append((field, old_value, new_value))
|
edits.append((field, old_value, new_value))
|
||||||
|
|
||||||
|
@ -487,6 +475,7 @@ class History:
|
||||||
# Revision history #
|
# Revision history #
|
||||||
############################
|
############################
|
||||||
|
|
||||||
|
|
||||||
class VersionAction(HistoryEvent):
|
class VersionAction(HistoryEvent):
|
||||||
def __init__(self, version):
|
def __init__(self, version):
|
||||||
self.version = version
|
self.version = version
|
||||||
|
@ -533,15 +522,14 @@ class VersionAction(HistoryEvent):
|
||||||
"""
|
"""
|
||||||
model = self.object_type()
|
model = self.object_type()
|
||||||
try:
|
try:
|
||||||
query = (
|
query = make_version_filter("pk", self.object_id()) & Q(
|
||||||
make_version_filter("pk", self.object_id())
|
revision__date_created__lt=self.version.revision.date_created
|
||||||
& Q(
|
)
|
||||||
revision__date_created__lt=self.version.revision.date_created
|
return (
|
||||||
)
|
Version.objects.get_for_model(model)
|
||||||
|
.filter(query)
|
||||||
|
.order_by("-revision__date_created")[0]
|
||||||
)
|
)
|
||||||
return (Version.objects.get_for_model(model)
|
|
||||||
.filter(query)
|
|
||||||
.order_by("-revision__date_created")[0])
|
|
||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -648,6 +636,7 @@ class ActionsSearch:
|
||||||
# Class-specific history #
|
# Class-specific history #
|
||||||
############################
|
############################
|
||||||
|
|
||||||
|
|
||||||
class UserHistoryEvent(HistoryEvent):
|
class UserHistoryEvent(HistoryEvent):
|
||||||
def _repr(self, name, value):
|
def _repr(self, name, value):
|
||||||
"""Get the appropriate representation of the given field.
|
"""Get the appropriate representation of the given field.
|
||||||
|
@ -733,13 +722,15 @@ class UserHistoryEvent(HistoryEvent):
|
||||||
)
|
)
|
||||||
|
|
||||||
def __hash__(self):
|
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):
|
def __repr__(self):
|
||||||
return "{} edited fields {} ({})".format(
|
return "{} edited fields {} ({})".format(
|
||||||
self.performed_by,
|
self.performed_by,
|
||||||
self.edited_fields or "nothing",
|
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
|
# Try to find an Adherent object
|
||||||
# If it exists, its id will be the same as the user's
|
# If it exists, its id will be the same as the user's
|
||||||
adherents = (
|
adherents = Version.objects.get_for_model(Adherent).filter(
|
||||||
Version.objects.get_for_model(Adherent)
|
make_version_filter("pk", user_id)
|
||||||
.filter(make_version_filter("pk", user_id))
|
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
obj = adherents[0]
|
obj = adherents[0]
|
||||||
|
@ -774,9 +764,8 @@ class UserHistory(History):
|
||||||
|
|
||||||
# Fallback on a Club
|
# Fallback on a Club
|
||||||
if obj is None:
|
if obj is None:
|
||||||
clubs = (
|
clubs = Version.objects.get_for_model(Club).filter(
|
||||||
Version.objects.get_for_model(Club)
|
make_version_filter("pk", user_id)
|
||||||
.filter(make_version_filter("pk", user_id))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -826,11 +815,7 @@ class UserHistory(History):
|
||||||
|
|
||||||
# Remove duplicates and sort
|
# Remove duplicates and sort
|
||||||
self.events = list(dict.fromkeys(self.events))
|
self.events = list(dict.fromkeys(self.events))
|
||||||
return sorted(
|
return sorted(self.events, key=lambda e: e.date, reverse=True)
|
||||||
self.events,
|
|
||||||
key=lambda e: e.date,
|
|
||||||
reverse=True
|
|
||||||
)
|
|
||||||
|
|
||||||
def _add_revision(self, version):
|
def _add_revision(self, version):
|
||||||
"""Add a new revision to the chronological order.
|
"""Add a new revision to the chronological order.
|
||||||
|
@ -843,7 +828,7 @@ class UserHistory(History):
|
||||||
diff = self._compute_diff(
|
diff = self._compute_diff(
|
||||||
version,
|
version,
|
||||||
self._last_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
|
# Ignore "empty" events like login
|
||||||
|
@ -973,7 +958,7 @@ HISTORY_CLASS_MAPPING = {
|
||||||
User: UserHistory,
|
User: UserHistory,
|
||||||
Machine: MachineHistory,
|
Machine: MachineHistory,
|
||||||
Interface: InterfaceHistory,
|
Interface: InterfaceHistory,
|
||||||
"default": History
|
"default": History,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -47,5 +47,9 @@ urlpatterns = [
|
||||||
path("stats_models", views.stats_models, name="stats-models"),
|
path("stats_models", views.stats_models, name="stats-models"),
|
||||||
path("stats_users", views.stats_users, name="stats-users"),
|
path("stats_users", views.stats_users, name="stats-users"),
|
||||||
path("stats_actions", views.stats_actions, name="stats-actions"),
|
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",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -38,84 +38,38 @@ objects for per model, number of actions per user etc.
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.urls import reverse
|
from django.apps import apps
|
||||||
from django.shortcuts import render, redirect
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.http import Http404
|
|
||||||
from django.db.models import Count
|
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 django.utils.translation import ugettext as _
|
||||||
|
from reversion.models import ContentType, Revision, Version
|
||||||
|
|
||||||
from reversion.models import Revision
|
from cotisations.models import (Article, Banque, Cotisation, Facture, Paiement,
|
||||||
from reversion.models import Version, ContentType
|
Vente)
|
||||||
|
from machines.models import (SOA, Domain, Extension, Interface, IpList, IpType,
|
||||||
from users.models import (
|
Machine, MachineType, Mx, Nas, Ns,
|
||||||
User,
|
OuverturePortList, Service, Vlan)
|
||||||
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 preferences.models import GeneralOption
|
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.views import form
|
||||||
from re2o.utils import (
|
from topologie.models import (AccessPoint, ConstructorSwitch, ModelSwitch,
|
||||||
all_whitelisted,
|
Port, Room, Stack, Switch)
|
||||||
all_baned,
|
from users.models import (Adherent, Ban, Club, ListRight, ListShell, School,
|
||||||
all_has_access,
|
ServiceUser, User, Whitelist)
|
||||||
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 .acl import can_view as can_view_logs
|
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
|
@login_required
|
||||||
|
@ -528,7 +482,11 @@ def stats_search_machine_history(request):
|
||||||
max_result = GeneralOption.get_cached_value("pagination_number")
|
max_result = GeneralOption.get_cached_value("pagination_number")
|
||||||
events = re2o_paginator(request, events, max_result)
|
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(
|
return render(
|
||||||
request, "logs/search_machine_history.html", {"history_form": history_form}
|
request, "logs/search_machine_history.html", {"history_form": history_form}
|
||||||
)
|
)
|
||||||
|
|
|
@ -29,24 +29,10 @@ from __future__ import unicode_literals
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from reversion.admin import VersionAdmin
|
from reversion.admin import VersionAdmin
|
||||||
|
|
||||||
from .models import (
|
from .models import (SOA, DName, Domain, Extension, Interface, IpList, IpType,
|
||||||
Extension,
|
Ipv6List, Machine, MachineType, Mx, Nas, Ns,
|
||||||
SOA,
|
OuverturePort, OuverturePortList, Role, Service, Srv,
|
||||||
Mx,
|
SshFp, Txt, Vlan)
|
||||||
Ns,
|
|
||||||
Vlan,
|
|
||||||
Txt,
|
|
||||||
DName,
|
|
||||||
Srv,
|
|
||||||
SshFp,
|
|
||||||
Nas,
|
|
||||||
Service,
|
|
||||||
Role,
|
|
||||||
OuverturePort,
|
|
||||||
Ipv6List,
|
|
||||||
OuverturePortList,
|
|
||||||
)
|
|
||||||
from .models import IpType, Machine, MachineType, Domain, IpList, Interface
|
|
||||||
|
|
||||||
|
|
||||||
class MachineAdmin(VersionAdmin):
|
class MachineAdmin(VersionAdmin):
|
||||||
|
|
|
@ -22,12 +22,12 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
import machines.models as machines
|
import machines.models as machines
|
||||||
from api.serializers import NamespacedHRField, NamespacedHIField, NamespacedHMSerializer
|
from api.serializers import (NamespacedHIField, NamespacedHMSerializer,
|
||||||
|
NamespacedHRField)
|
||||||
|
|
||||||
|
|
||||||
class MachineSerializer(NamespacedHMSerializer):
|
class MachineSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.Machine` objects.
|
"""Serialize `machines.models.Machine` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.Machine
|
model = machines.Machine
|
||||||
|
@ -35,8 +35,7 @@ class MachineSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class MachineTypeSerializer(NamespacedHMSerializer):
|
class MachineTypeSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.MachineType` objects.
|
"""Serialize `machines.models.MachineType` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.MachineType
|
model = machines.MachineType
|
||||||
|
@ -44,8 +43,7 @@ class MachineTypeSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class IpTypeSerializer(NamespacedHMSerializer):
|
class IpTypeSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.IpType` objects.
|
"""Serialize `machines.models.IpType` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.IpType
|
model = machines.IpType
|
||||||
|
@ -63,8 +61,7 @@ class IpTypeSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class VlanSerializer(NamespacedHMSerializer):
|
class VlanSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.Vlan` objects.
|
"""Serialize `machines.models.Vlan` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.Vlan
|
model = machines.Vlan
|
||||||
|
@ -82,8 +79,7 @@ class VlanSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class NasSerializer(NamespacedHMSerializer):
|
class NasSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.Nas` objects.
|
"""Serialize `machines.models.Nas` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.Nas
|
model = machines.Nas
|
||||||
|
@ -98,8 +94,7 @@ class NasSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class SOASerializer(NamespacedHMSerializer):
|
class SOASerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.SOA` objects.
|
"""Serialize `machines.models.SOA` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.SOA
|
model = machines.SOA
|
||||||
|
@ -107,8 +102,7 @@ class SOASerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class ExtensionSerializer(NamespacedHMSerializer):
|
class ExtensionSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize machines.models.Extension objects.
|
"""Serialize machines.models.Extension objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.Extension
|
model = machines.Extension
|
||||||
|
@ -116,8 +110,7 @@ class ExtensionSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class MxSerializer(NamespacedHMSerializer):
|
class MxSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.Mx` objects.
|
"""Serialize `machines.models.Mx` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.Mx
|
model = machines.Mx
|
||||||
|
@ -125,8 +118,7 @@ class MxSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class DNameSerializer(NamespacedHMSerializer):
|
class DNameSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.DName` objects.
|
"""Serialize `machines.models.DName` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.DName
|
model = machines.DName
|
||||||
|
@ -134,8 +126,7 @@ class DNameSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class NsSerializer(NamespacedHMSerializer):
|
class NsSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.Ns` objects.
|
"""Serialize `machines.models.Ns` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.Ns
|
model = machines.Ns
|
||||||
|
@ -143,8 +134,7 @@ class NsSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class TxtSerializer(NamespacedHMSerializer):
|
class TxtSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.Txt` objects.
|
"""Serialize `machines.models.Txt` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.Txt
|
model = machines.Txt
|
||||||
|
@ -152,8 +142,7 @@ class TxtSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class SrvSerializer(NamespacedHMSerializer):
|
class SrvSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.Srv` objects.
|
"""Serialize `machines.models.Srv` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.Srv
|
model = machines.Srv
|
||||||
|
@ -171,8 +160,7 @@ class SrvSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class SshFpSerializer(NamespacedHMSerializer):
|
class SshFpSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.SSHFP` objects.
|
"""Serialize `machines.models.SSHFP` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.SshFp
|
model = machines.SshFp
|
||||||
|
@ -180,8 +168,7 @@ class SshFpSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class InterfaceSerializer(NamespacedHMSerializer):
|
class InterfaceSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.Interface` objects.
|
"""Serialize `machines.models.Interface` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
mac_address = serializers.CharField()
|
mac_address = serializers.CharField()
|
||||||
active = serializers.BooleanField(source="is_active")
|
active = serializers.BooleanField(source="is_active")
|
||||||
|
@ -201,8 +188,7 @@ class InterfaceSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class Ipv6ListSerializer(NamespacedHMSerializer):
|
class Ipv6ListSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.Ipv6List` objects.
|
"""Serialize `machines.models.Ipv6List` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.Ipv6List
|
model = machines.Ipv6List
|
||||||
|
@ -210,8 +196,7 @@ class Ipv6ListSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class DomainSerializer(NamespacedHMSerializer):
|
class DomainSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.Domain` objects.
|
"""Serialize `machines.models.Domain` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.Domain
|
model = machines.Domain
|
||||||
|
@ -219,8 +204,7 @@ class DomainSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class IpListSerializer(NamespacedHMSerializer):
|
class IpListSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.IpList` objects.
|
"""Serialize `machines.models.IpList` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.IpList
|
model = machines.IpList
|
||||||
|
@ -228,8 +212,7 @@ class IpListSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class ServiceSerializer(NamespacedHMSerializer):
|
class ServiceSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.Service` objects.
|
"""Serialize `machines.models.Service` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.Service
|
model = machines.Service
|
||||||
|
@ -243,8 +226,7 @@ class ServiceSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class ServiceLinkSerializer(NamespacedHMSerializer):
|
class ServiceLinkSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.Service_link` objects.
|
"""Serialize `machines.models.Service_link` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.Service_link
|
model = machines.Service_link
|
||||||
|
@ -260,8 +242,7 @@ class ServiceLinkSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class OuverturePortListSerializer(NamespacedHMSerializer):
|
class OuverturePortListSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.OuverturePortList` objects.
|
"""Serialize `machines.models.OuverturePortList` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
tcp_ports_in = NamespacedHRField(
|
tcp_ports_in = NamespacedHRField(
|
||||||
view_name="ouvertureport-detail", many=True, read_only=True
|
view_name="ouvertureport-detail", many=True, read_only=True
|
||||||
|
@ -289,8 +270,7 @@ class OuverturePortListSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class OuverturePortSerializer(NamespacedHMSerializer):
|
class OuverturePortSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.OuverturePort` objects.
|
"""Serialize `machines.models.OuverturePort` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.OuverturePort
|
model = machines.OuverturePort
|
||||||
|
@ -298,8 +278,7 @@ class OuverturePortSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class RoleSerializer(NamespacedHMSerializer):
|
class RoleSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `machines.models.OuverturePort` objects.
|
"""Serialize `machines.models.OuverturePort` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
servers = InterfaceSerializer(read_only=True, many=True)
|
servers = InterfaceSerializer(read_only=True, many=True)
|
||||||
|
|
||||||
|
@ -309,8 +288,7 @@ class RoleSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class ServiceRegenSerializer(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)
|
hostname = serializers.CharField(source="server.domain.name", read_only=True)
|
||||||
service_name = serializers.CharField(source="service.service_type", 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):
|
class DNSZonesSerializer(serializers.ModelSerializer):
|
||||||
"""Serialize the data about DNS Zones.
|
"""Serialize the data about DNS Zones."""
|
||||||
"""
|
|
||||||
|
|
||||||
soa = SOARecordSerializer()
|
soa = SOARecordSerializer()
|
||||||
ns_records = NSRecordSerializer(many=True, source="ns_set")
|
ns_records = NSRecordSerializer(many=True, source="ns_set")
|
||||||
|
@ -559,8 +536,7 @@ class DNSZonesSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class DNSReverseZonesSerializer(serializers.ModelSerializer):
|
class DNSReverseZonesSerializer(serializers.ModelSerializer):
|
||||||
"""Serialize the data about DNS Zones.
|
"""Serialize the data about DNS Zones."""
|
||||||
"""
|
|
||||||
|
|
||||||
soa = SOARecordSerializer(source="extension.soa")
|
soa = SOARecordSerializer(source="extension.soa")
|
||||||
extension = serializers.CharField(source="extension.name", read_only=True)
|
extension = serializers.CharField(source="extension.name", read_only=True)
|
||||||
|
|
|
@ -45,9 +45,8 @@ urls_viewset = [
|
||||||
(r"machines/ouvertureport", views.OuverturePortViewSet, None),
|
(r"machines/ouvertureport", views.OuverturePortViewSet, None),
|
||||||
(r"machines/role", views.RoleViewSet, None),
|
(r"machines/role", views.RoleViewSet, None),
|
||||||
(r"machines/services-regen", views.ServiceRegenViewSet, "serviceregen"),
|
(r"machines/services-regen", views.ServiceRegenViewSet, "serviceregen"),
|
||||||
|
|
||||||
# Deprecated
|
# Deprecated
|
||||||
(r"services/regen", views.ServiceRegenViewSet, "serviceregen")
|
(r"services/regen", views.ServiceRegenViewSet, "serviceregen"),
|
||||||
]
|
]
|
||||||
|
|
||||||
urls_view = [
|
urls_view = [
|
||||||
|
@ -56,11 +55,10 @@ urls_view = [
|
||||||
(r"machines/firewall-interface-ports", views.InterfacePortsOpenView),
|
(r"machines/firewall-interface-ports", views.InterfacePortsOpenView),
|
||||||
(r"machines/dns-zones", views.DNSZonesView),
|
(r"machines/dns-zones", views.DNSZonesView),
|
||||||
(r"machines/dns-reverse-zones", views.DNSReverseZonesView),
|
(r"machines/dns-reverse-zones", views.DNSReverseZonesView),
|
||||||
|
|
||||||
# Deprecated
|
# Deprecated
|
||||||
(r"dhcp/hostmacip", views.HostMacIpView),
|
(r"dhcp/hostmacip", views.HostMacIpView),
|
||||||
(r"firewall/subnet-ports", views.SubnetPortsOpenView),
|
(r"firewall/subnet-ports", views.SubnetPortsOpenView),
|
||||||
(r"firewall/interface-ports", views.InterfacePortsOpenView),
|
(r"firewall/interface-ports", views.InterfacePortsOpenView),
|
||||||
(r"dns/zones", views.DNSZonesView),
|
(r"dns/zones", views.DNSZonesView),
|
||||||
(r"dns/reverse-zones", views.DNSReverseZonesView),
|
(r"dns/reverse-zones", views.DNSReverseZonesView),
|
||||||
]
|
]
|
||||||
|
|
|
@ -19,159 +19,142 @@
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 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
|
import machines.models as machines
|
||||||
from re2o.utils import all_active_interfaces
|
from re2o.utils import all_active_interfaces
|
||||||
|
|
||||||
|
from . import serializers
|
||||||
|
|
||||||
|
|
||||||
class MachineViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = machines.Machine.objects.all()
|
||||||
serializer_class = serializers.MachineSerializer
|
serializer_class = serializers.MachineSerializer
|
||||||
|
|
||||||
|
|
||||||
class MachineTypeViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = machines.MachineType.objects.all()
|
||||||
serializer_class = serializers.MachineTypeSerializer
|
serializer_class = serializers.MachineTypeSerializer
|
||||||
|
|
||||||
|
|
||||||
class IpTypeViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = machines.IpType.objects.all()
|
||||||
serializer_class = serializers.IpTypeSerializer
|
serializer_class = serializers.IpTypeSerializer
|
||||||
|
|
||||||
|
|
||||||
class VlanViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = machines.Vlan.objects.all()
|
||||||
serializer_class = serializers.VlanSerializer
|
serializer_class = serializers.VlanSerializer
|
||||||
|
|
||||||
|
|
||||||
class NasViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = machines.Nas.objects.all()
|
||||||
serializer_class = serializers.NasSerializer
|
serializer_class = serializers.NasSerializer
|
||||||
|
|
||||||
|
|
||||||
class SOAViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = machines.SOA.objects.all()
|
||||||
serializer_class = serializers.SOASerializer
|
serializer_class = serializers.SOASerializer
|
||||||
|
|
||||||
|
|
||||||
class ExtensionViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = machines.Extension.objects.all()
|
||||||
serializer_class = serializers.ExtensionSerializer
|
serializer_class = serializers.ExtensionSerializer
|
||||||
|
|
||||||
|
|
||||||
class MxViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = machines.Mx.objects.all()
|
||||||
serializer_class = serializers.MxSerializer
|
serializer_class = serializers.MxSerializer
|
||||||
|
|
||||||
|
|
||||||
class NsViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = machines.Ns.objects.all()
|
||||||
serializer_class = serializers.NsSerializer
|
serializer_class = serializers.NsSerializer
|
||||||
|
|
||||||
|
|
||||||
class TxtViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = machines.Txt.objects.all()
|
||||||
serializer_class = serializers.TxtSerializer
|
serializer_class = serializers.TxtSerializer
|
||||||
|
|
||||||
|
|
||||||
class DNameViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = machines.DName.objects.all()
|
||||||
serializer_class = serializers.DNameSerializer
|
serializer_class = serializers.DNameSerializer
|
||||||
|
|
||||||
|
|
||||||
class SrvViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = machines.Srv.objects.all()
|
||||||
serializer_class = serializers.SrvSerializer
|
serializer_class = serializers.SrvSerializer
|
||||||
|
|
||||||
|
|
||||||
class SshFpViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = machines.SshFp.objects.all()
|
||||||
serializer_class = serializers.SshFpSerializer
|
serializer_class = serializers.SshFpSerializer
|
||||||
|
|
||||||
|
|
||||||
class InterfaceViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = machines.Interface.objects.all()
|
||||||
serializer_class = serializers.InterfaceSerializer
|
serializer_class = serializers.InterfaceSerializer
|
||||||
|
|
||||||
|
|
||||||
class Ipv6ListViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = machines.Ipv6List.objects.all()
|
||||||
serializer_class = serializers.Ipv6ListSerializer
|
serializer_class = serializers.Ipv6ListSerializer
|
||||||
|
|
||||||
|
|
||||||
class DomainViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = machines.Domain.objects.all()
|
||||||
serializer_class = serializers.DomainSerializer
|
serializer_class = serializers.DomainSerializer
|
||||||
|
|
||||||
|
|
||||||
class IpListViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = machines.IpList.objects.all()
|
||||||
serializer_class = serializers.IpListSerializer
|
serializer_class = serializers.IpListSerializer
|
||||||
|
|
||||||
|
|
||||||
class ServiceViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = machines.Service.objects.all()
|
||||||
serializer_class = serializers.ServiceSerializer
|
serializer_class = serializers.ServiceSerializer
|
||||||
|
|
||||||
|
|
||||||
class ServiceLinkViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = machines.Service_link.objects.all()
|
||||||
serializer_class = serializers.ServiceLinkSerializer
|
serializer_class = serializers.ServiceLinkSerializer
|
||||||
|
@ -187,24 +170,21 @@ class OuverturePortListViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
|
|
||||||
|
|
||||||
class OuverturePortViewSet(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()
|
queryset = machines.OuverturePort.objects.all()
|
||||||
serializer_class = serializers.OuverturePortSerializer
|
serializer_class = serializers.OuverturePortSerializer
|
||||||
|
|
||||||
|
|
||||||
class RoleViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = machines.Role.objects.all()
|
||||||
serializer_class = serializers.RoleSerializer
|
serializer_class = serializers.RoleSerializer
|
||||||
|
|
||||||
|
|
||||||
class ServiceRegenViewSet(viewsets.ModelViewSet):
|
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
|
serializer_class = serializers.ServiceRegenSerializer
|
||||||
|
|
||||||
|
@ -238,6 +218,7 @@ class InterfacePortsOpenView(generics.ListAPIView):
|
||||||
queryset = machines.Interface.objects.filter(port_lists__isnull=False).distinct()
|
queryset = machines.Interface.objects.filter(port_lists__isnull=False).distinct()
|
||||||
serializer_class = serializers.InterfacePortsOpenSerializer
|
serializer_class = serializers.InterfacePortsOpenSerializer
|
||||||
|
|
||||||
|
|
||||||
class DNSZonesView(generics.ListAPIView):
|
class DNSZonesView(generics.ListAPIView):
|
||||||
"""Exposes the detailed information about each extension (hostnames,
|
"""Exposes the detailed information about each extension (hostnames,
|
||||||
IPs, DNS records, etc.) in order to build the DNS zone files.
|
IPs, DNS records, etc.) in order to build the DNS zone files.
|
||||||
|
@ -264,4 +245,4 @@ class DNSReverseZonesView(generics.ListAPIView):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
queryset = machines.IpType.objects.all()
|
queryset = machines.IpType.objects.all()
|
||||||
serializer_class = serializers.DNSReverseZonesSerializer
|
serializer_class = serializers.DNSReverseZonesSerializer
|
||||||
|
|
|
@ -8,4 +8,4 @@ from django.apps import AppConfig
|
||||||
class MachinesConfig(AppConfig):
|
class MachinesConfig(AppConfig):
|
||||||
"""Configuration of machines app."""
|
"""Configuration of machines app."""
|
||||||
|
|
||||||
name = "machines"
|
name = "machines"
|
||||||
|
|
|
@ -36,37 +36,17 @@ Forms to create, edit and delete:
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django import forms
|
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 django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from re2o.field_permissions import FieldPermissionFormMixin
|
from re2o.field_permissions import FieldPermissionFormMixin
|
||||||
from re2o.mixins import FormRevMixin
|
from re2o.mixins import FormRevMixin
|
||||||
from re2o.widgets import (
|
from re2o.widgets import (AutocompleteModelWidget,
|
||||||
AutocompleteModelWidget,
|
AutocompleteMultipleModelWidget)
|
||||||
AutocompleteMultipleModelWidget,
|
|
||||||
)
|
from .models import (SOA, DName, Domain, Extension, Interface, IpList, IpType,
|
||||||
from .models import (
|
Ipv6List, Machine, MachineType, Mx, Nas, Ns,
|
||||||
Domain,
|
OuverturePortList, Role, Service, Srv, SshFp, Txt, Vlan)
|
||||||
Machine,
|
|
||||||
Interface,
|
|
||||||
IpList,
|
|
||||||
MachineType,
|
|
||||||
Extension,
|
|
||||||
SOA,
|
|
||||||
Mx,
|
|
||||||
Txt,
|
|
||||||
DName,
|
|
||||||
Ns,
|
|
||||||
Role,
|
|
||||||
Service,
|
|
||||||
Vlan,
|
|
||||||
Srv,
|
|
||||||
SshFp,
|
|
||||||
Nas,
|
|
||||||
IpType,
|
|
||||||
OuverturePortList,
|
|
||||||
Ipv6List,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class EditMachineForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
|
class EditMachineForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
|
||||||
|
@ -248,7 +228,9 @@ class IpTypeForm(FormRevMixin, ModelForm):
|
||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
widgets = {
|
widgets = {
|
||||||
"vlan": AutocompleteModelWidget(url="/machines/vlan-autocomplete"),
|
"vlan": AutocompleteModelWidget(url="/machines/vlan-autocomplete"),
|
||||||
"extension": AutocompleteModelWidget(url="/machines/extension-autocomplete"),
|
"extension": AutocompleteModelWidget(
|
||||||
|
url="/machines/extension-autocomplete"
|
||||||
|
),
|
||||||
"ouverture_ports": AutocompleteModelWidget(
|
"ouverture_ports": AutocompleteModelWidget(
|
||||||
url="/machines/ouvertureportlist-autocomplete"
|
url="/machines/ouvertureportlist-autocomplete"
|
||||||
),
|
),
|
||||||
|
@ -525,7 +507,9 @@ class SrvForm(FormRevMixin, ModelForm):
|
||||||
model = Srv
|
model = Srv
|
||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
widgets = {
|
widgets = {
|
||||||
"extension": AutocompleteModelWidget(url="/machines/extension-autocomplete"),
|
"extension": AutocompleteModelWidget(
|
||||||
|
url="/machines/extension-autocomplete"
|
||||||
|
),
|
||||||
"target": AutocompleteModelWidget(url="/machines/domain-autocomplete"),
|
"target": AutocompleteModelWidget(url="/machines/domain-autocomplete"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,26 +35,18 @@ from ipaddress import IPv6Address
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
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 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.dispatch import receiver
|
||||||
from django.forms import ValidationError
|
from django.forms import ValidationError
|
||||||
from django.utils import timezone
|
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.functional import cached_property
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from macaddress.fields import MACAddressField, default_dialect
|
from macaddress.fields import MACAddressField, default_dialect
|
||||||
from netaddr import (
|
from netaddr import (EUI, IPAddress, IPNetwork, IPRange, IPSet,
|
||||||
mac_bare,
|
NotRegisteredError, mac_bare)
|
||||||
EUI,
|
from reversion import revisions as reversion
|
||||||
NotRegisteredError,
|
|
||||||
IPSet,
|
|
||||||
IPRange,
|
|
||||||
IPNetwork,
|
|
||||||
IPAddress,
|
|
||||||
)
|
|
||||||
|
|
||||||
import preferences.models
|
import preferences.models
|
||||||
import users.models
|
import users.models
|
||||||
|
@ -79,9 +71,7 @@ class Machine(RevMixin, FieldPermissionModelMixin, AclMixin, models.Model):
|
||||||
active = models.BooleanField(default=True)
|
active = models.BooleanField(default=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (("change_machine_user", _("Can change the user of a machine")),)
|
||||||
("change_machine_user", _("Can change the user of a machine")),
|
|
||||||
)
|
|
||||||
verbose_name = _("machine")
|
verbose_name = _("machine")
|
||||||
verbose_name_plural = _("machines")
|
verbose_name_plural = _("machines")
|
||||||
|
|
||||||
|
@ -342,9 +332,7 @@ class MachineType(RevMixin, AclMixin, models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (("use_all_machinetype", _("Can use all machine types")),)
|
||||||
("use_all_machinetype", _("Can use all machine types")),
|
|
||||||
)
|
|
||||||
verbose_name = _("machine type")
|
verbose_name = _("machine type")
|
||||||
verbose_name_plural = _("machine types")
|
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.
|
"""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)
|
(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
|
@staticmethod
|
||||||
def can_use_all(user_request, *_args, **_kwargs):
|
def can_use_all(user_request, *_args, **_kwargs):
|
||||||
|
@ -379,7 +369,7 @@ class MachineType(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_list(cls, user_request, *_args, **_kwargs):
|
def can_list(cls, user_request, *_args, **_kwargs):
|
||||||
"""All users can list unprivileged machinetypes
|
"""All users can list unprivileged machinetypes
|
||||||
Only members of privileged groups can list all.
|
Only members of privileged groups can list all.
|
||||||
|
|
||||||
:param user_request: The user who wants to view the list.
|
:param user_request: The user who wants to view the list.
|
||||||
|
@ -389,20 +379,13 @@ class MachineType(RevMixin, AclMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
can, _message, _group = cls.can_use_all(user_request)
|
can, _message, _group = cls.can_use_all(user_request)
|
||||||
if can:
|
if can:
|
||||||
return (
|
return (True, None, None, cls.objects.all())
|
||||||
True,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
cls.objects.all()
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
return (
|
return (
|
||||||
True,
|
True,
|
||||||
_("You don't have the right to use all machine types."),
|
_("You don't have the right to use all machine types."),
|
||||||
("machines.use_all_machinetype",),
|
("machines.use_all_machinetype",),
|
||||||
cls.objects.filter(
|
cls.objects.filter(ip_type__in=IpType.objects.filter(need_infra=False)),
|
||||||
ip_type__in=IpType.objects.filter(need_infra=False)
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -455,12 +438,12 @@ class IpType(RevMixin, AclMixin, models.Model):
|
||||||
default=False, help_text=_("Enable reverse DNS for IPv6.")
|
default=False, help_text=_("Enable reverse DNS for IPv6.")
|
||||||
)
|
)
|
||||||
vlan = models.ForeignKey("Vlan", on_delete=models.PROTECT, blank=True, null=True)
|
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:
|
class Meta:
|
||||||
permissions = (
|
permissions = (("use_all_iptype", _("Can use all IP types")),)
|
||||||
("use_all_iptype", _("Can use all IP types")),
|
|
||||||
)
|
|
||||||
verbose_name = _("IP type")
|
verbose_name = _("IP type")
|
||||||
verbose_name_plural = _("IP types")
|
verbose_name_plural = _("IP types")
|
||||||
|
|
||||||
|
@ -897,9 +880,7 @@ class Extension(RevMixin, AclMixin, models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (("use_all_extension", _("Can use all extensions")),)
|
||||||
("use_all_extension", _("Can use all extensions")),
|
|
||||||
)
|
|
||||||
verbose_name = _("DNS extension")
|
verbose_name = _("DNS extension")
|
||||||
verbose_name_plural = _("DNS extensions")
|
verbose_name_plural = _("DNS extensions")
|
||||||
|
|
||||||
|
@ -977,7 +958,7 @@ class Extension(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_list(cls, user_request, *_args, **_kwargs):
|
def can_list(cls, user_request, *_args, **_kwargs):
|
||||||
"""All users can list unprivileged extensions
|
"""All users can list unprivileged extensions
|
||||||
Only members of privileged groups can list all.
|
Only members of privileged groups can list all.
|
||||||
|
|
||||||
:param user_request: The user who wants to view the list.
|
:param user_request: The user who wants to view the list.
|
||||||
|
@ -987,12 +968,7 @@ class Extension(RevMixin, AclMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
can, _message, _group = cls.can_use_all(user_request)
|
can, _message, _group = cls.can_use_all(user_request)
|
||||||
if can:
|
if can:
|
||||||
return (
|
return (True, None, None, cls.objects.all())
|
||||||
True,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
cls.objects.all()
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
return (
|
return (
|
||||||
True,
|
True,
|
||||||
|
@ -1035,8 +1011,7 @@ class Mx(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def dns_entry(self):
|
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(
|
return "@ IN MX {prior} {name}".format(
|
||||||
prior=str(self.priority).ljust(3), name=str(self.name)
|
prior=str(self.priority).ljust(3), name=str(self.name)
|
||||||
)
|
)
|
||||||
|
@ -1066,8 +1041,7 @@ class Ns(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def dns_entry(self):
|
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)
|
return "@ IN NS " + str(self.ns)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -1100,8 +1074,7 @@ class Txt(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def dns_entry(self):
|
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)
|
return str(self.field1).ljust(15) + " IN TXT " + str(self.field2)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1129,8 +1102,7 @@ class DName(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def dns_entry(self):
|
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)
|
return str(self.alias).ljust(15) + " IN DNAME " + str(self.zone)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1204,8 +1176,7 @@ class Srv(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def dns_entry(self):
|
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 (
|
return (
|
||||||
str(self.service)
|
str(self.service)
|
||||||
+ "._"
|
+ "._"
|
||||||
|
@ -1387,8 +1358,7 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
return vendor
|
return vendor
|
||||||
|
|
||||||
def sync_ipv6_dhcpv6(self):
|
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
|
ipv6_dhcpv6 = self.gen_ipv6_dhcpv6
|
||||||
if not ipv6_dhcpv6:
|
if not ipv6_dhcpv6:
|
||||||
return
|
return
|
||||||
|
@ -1414,8 +1384,7 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
ipv6_object.save()
|
ipv6_object.save()
|
||||||
|
|
||||||
def sync_ipv6(self):
|
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":
|
if preferences.models.OptionalMachine.get_cached_value("ipv6_mode") == "SLAAC":
|
||||||
self.sync_ipv6_slaac()
|
self.sync_ipv6_slaac()
|
||||||
elif (
|
elif (
|
||||||
|
@ -1581,8 +1550,10 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
_("You don't have the right to add a machine."),
|
_("You don't have the right to add a machine."),
|
||||||
("machines.add_interface",),
|
("machines.add_interface",),
|
||||||
)
|
)
|
||||||
max_lambdauser_interfaces = preferences.models.OptionalMachine.get_cached_value(
|
max_lambdauser_interfaces = (
|
||||||
"max_lambdauser_interfaces"
|
preferences.models.OptionalMachine.get_cached_value(
|
||||||
|
"max_lambdauser_interfaces"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
if machine.user != user_request:
|
if machine.user != user_request:
|
||||||
return (
|
return (
|
||||||
|
@ -1721,8 +1692,7 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
)
|
)
|
||||||
slaac_ip = models.BooleanField(default=False)
|
slaac_ip = models.BooleanField(default=False)
|
||||||
active = models.BooleanField(
|
active = models.BooleanField(
|
||||||
default=True,
|
default=True, help_text=_("If false,the DNS will not provide this ip.")
|
||||||
help_text=_("If false,the DNS will not provide this ip.")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -1856,8 +1826,9 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
|
|
||||||
def check_and_replace_prefix(self, prefix=None):
|
def check_and_replace_prefix(self, prefix=None):
|
||||||
"""Check if the IPv6 prefix is correct and update it if not."""
|
"""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(
|
prefix_v6 = (
|
||||||
"utf-8"
|
prefix
|
||||||
|
or self.interface.machine_type.ip_type.prefix_v6.encode().decode("utf-8")
|
||||||
)
|
)
|
||||||
if not prefix_v6:
|
if not prefix_v6:
|
||||||
return
|
return
|
||||||
|
@ -1930,7 +1901,11 @@ class Domain(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
)
|
)
|
||||||
extension = models.ForeignKey("Extension", on_delete=models.PROTECT)
|
extension = models.ForeignKey("Extension", on_delete=models.PROTECT)
|
||||||
cname = models.ForeignKey(
|
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(
|
ttl = models.PositiveIntegerField(
|
||||||
verbose_name=_("Time To Live (TTL)"),
|
verbose_name=_("Time To Live (TTL)"),
|
||||||
|
@ -1940,9 +1915,7 @@ class Domain(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = (("name", "extension"),)
|
unique_together = (("name", "extension"),)
|
||||||
permissions = (
|
permissions = (("change_ttl", _("Can change the TTL of a domain object")),)
|
||||||
("change_ttl", _("Can change the TTL of a domain object")),
|
|
||||||
)
|
|
||||||
verbose_name = _("domain")
|
verbose_name = _("domain")
|
||||||
verbose_name_plural = _("domains")
|
verbose_name_plural = _("domains")
|
||||||
|
|
||||||
|
@ -2037,8 +2010,10 @@ class Domain(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
except Interface.DoesNotExist:
|
except Interface.DoesNotExist:
|
||||||
return False, _("Nonexistent interface."), None
|
return False, _("Nonexistent interface."), None
|
||||||
if not user_request.has_perm("machines.add_domain"):
|
if not user_request.has_perm("machines.add_domain"):
|
||||||
max_lambdauser_aliases = preferences.models.OptionalMachine.get_cached_value(
|
max_lambdauser_aliases = (
|
||||||
"max_lambdauser_aliases"
|
preferences.models.OptionalMachine.get_cached_value(
|
||||||
|
"max_lambdauser_aliases"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
if interface.machine.user != user_request:
|
if interface.machine.user != user_request:
|
||||||
return (
|
return (
|
||||||
|
@ -2173,8 +2148,7 @@ class IpList(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def need_infra(self):
|
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
|
return self.ip_type.need_infra
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
@ -2206,20 +2180,13 @@ class IpList(RevMixin, AclMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
can, _message, _group = IpType.can_use_all(user_request)
|
can, _message, _group = IpType.can_use_all(user_request)
|
||||||
if can:
|
if can:
|
||||||
return (
|
return (True, None, None, cls.objects.all())
|
||||||
True,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
cls.objects.all()
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
return (
|
return (
|
||||||
True,
|
True,
|
||||||
_("You don't have the right to use all machine types."),
|
_("You don't have the right to use all machine types."),
|
||||||
("machines.use_all_machinetype",),
|
("machines.use_all_machinetype",),
|
||||||
cls.objects.filter(
|
cls.objects.filter(ip_type__in=IpType.objects.filter(need_infra=False)),
|
||||||
ip_type__in=IpType.objects.filter(need_infra=False)
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -2517,7 +2484,13 @@ class OuverturePort(RevMixin, AclMixin, models.Model):
|
||||||
def machine_post_save(**kwargs):
|
def machine_post_save(**kwargs):
|
||||||
"""Synchronise LDAP and regen firewall/DHCP after a machine is edited."""
|
"""Synchronise LDAP and regen firewall/DHCP after a machine is edited."""
|
||||||
user = kwargs["instance"].user
|
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("dhcp")
|
||||||
regen("mac_ip_list")
|
regen("mac_ip_list")
|
||||||
|
|
||||||
|
@ -2527,7 +2500,13 @@ def machine_post_delete(**kwargs):
|
||||||
"""Synchronise LDAP and regen firewall/DHCP after a machine is deleted."""
|
"""Synchronise LDAP and regen firewall/DHCP after a machine is deleted."""
|
||||||
machine = kwargs["instance"]
|
machine = kwargs["instance"]
|
||||||
user = machine.user
|
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("dhcp")
|
||||||
regen("mac_ip_list")
|
regen("mac_ip_list")
|
||||||
|
|
||||||
|
@ -2540,7 +2519,13 @@ def interface_post_save(**kwargs):
|
||||||
interface = kwargs["instance"]
|
interface = kwargs["instance"]
|
||||||
interface.sync_ipv6()
|
interface.sync_ipv6()
|
||||||
user = interface.machine.user
|
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 services
|
||||||
regen("dhcp")
|
regen("dhcp")
|
||||||
regen("mac_ip_list")
|
regen("mac_ip_list")
|
||||||
|
@ -2552,11 +2537,16 @@ def interface_post_save(**kwargs):
|
||||||
|
|
||||||
@receiver(post_delete, sender=Interface)
|
@receiver(post_delete, sender=Interface)
|
||||||
def interface_post_delete(**kwargs):
|
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"]
|
interface = kwargs["instance"]
|
||||||
user = interface.machine.user
|
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)
|
@receiver(post_save, sender=IpType)
|
||||||
|
|
|
@ -28,8 +28,7 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from . import views
|
from . import views, views_autocomplete
|
||||||
from . import views_autocomplete
|
|
||||||
|
|
||||||
app_name = "machines"
|
app_name = "machines"
|
||||||
|
|
||||||
|
@ -96,9 +95,7 @@ urlpatterns = [
|
||||||
path("add_alias/<int:interfaceid>", views.add_alias, name="add-alias"),
|
path("add_alias/<int:interfaceid>", views.add_alias, name="add-alias"),
|
||||||
path("edit_alias/<int:domainid>", views.edit_alias, name="edit-alias"),
|
path("edit_alias/<int:domainid>", views.edit_alias, name="edit-alias"),
|
||||||
path("del_alias/<int:interfaceid>", views.del_alias, name="del-alias"),
|
path("del_alias/<int:interfaceid>", views.del_alias, name="del-alias"),
|
||||||
path(
|
path("index_alias/<int:interfaceid>", views.index_alias, name="index-alias"),
|
||||||
"index_alias/<int:interfaceid>", views.index_alias, name="index-alias"
|
|
||||||
),
|
|
||||||
path(
|
path(
|
||||||
"new_ipv6list/<int:interfaceid>",
|
"new_ipv6list/<int:interfaceid>",
|
||||||
views.new_ipv6list,
|
views.new_ipv6list,
|
||||||
|
@ -116,9 +113,7 @@ urlpatterns = [
|
||||||
),
|
),
|
||||||
path("index_ipv6/<int:interfaceid>", views.index_ipv6, name="index-ipv6"),
|
path("index_ipv6/<int:interfaceid>", views.index_ipv6, name="index-ipv6"),
|
||||||
path("add_service", views.add_service, name="add-service"),
|
path("add_service", views.add_service, name="add-service"),
|
||||||
path(
|
path("edit_service/<int:serviceid>", views.edit_service, name="edit-service"),
|
||||||
"edit_service/<int:serviceid>", views.edit_service, name="edit-service"
|
|
||||||
),
|
|
||||||
path("del_service", views.del_service, name="del-service"),
|
path("del_service", views.del_service, name="del-service"),
|
||||||
path(
|
path(
|
||||||
"regen_service/<int:serviceid>",
|
"regen_service/<int:serviceid>",
|
||||||
|
@ -157,13 +152,49 @@ urlpatterns = [
|
||||||
name="port-config",
|
name="port-config",
|
||||||
),
|
),
|
||||||
### Autocomplete Views
|
### Autocomplete Views
|
||||||
path('vlan-autocomplete', views_autocomplete.VlanAutocomplete.as_view(), name='vlan-autocomplete',),
|
path(
|
||||||
path('interface-autocomplete', views_autocomplete.InterfaceAutocomplete.as_view(), name='interface-autocomplete',),
|
"vlan-autocomplete",
|
||||||
path('machine-autocomplete', views_autocomplete.MachineAutocomplete.as_view(), name='machine-autocomplete',),
|
views_autocomplete.VlanAutocomplete.as_view(),
|
||||||
path('machinetype-autocomplete', views_autocomplete.MachineTypeAutocomplete.as_view(), name='machinetype-autocomplete',),
|
name="vlan-autocomplete",
|
||||||
path('iptype-autocomplete', views_autocomplete.IpTypeAutocomplete.as_view(), name='iptype-autocomplete',),
|
),
|
||||||
path('extension-autocomplete', views_autocomplete.ExtensionAutocomplete.as_view(), name='extension-autocomplete',),
|
path(
|
||||||
path('domain-autocomplete', views_autocomplete.DomainAutocomplete.as_view(), name='domain-autocomplete',),
|
"interface-autocomplete",
|
||||||
path('ouvertureportlist-autocomplete', views_autocomplete.OuverturePortListAutocomplete.as_view(), name='ouvertureportlist-autocomplete',),
|
views_autocomplete.InterfaceAutocomplete.as_view(),
|
||||||
path('iplist-autocomplete', views_autocomplete.IpListAutocomplete.as_view(), name='iplist-autocomplete',),
|
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",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -34,11 +34,11 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required, permission_required
|
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 import IntegrityError
|
||||||
|
from django.db.models import F, ProtectedError
|
||||||
from django.forms import modelformset_factory
|
from django.forms import modelformset_factory
|
||||||
from django.http import HttpResponse
|
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.template.loader import render_to_string
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import ugettext as _
|
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 rest_framework.renderers import JSONRenderer
|
||||||
|
|
||||||
from preferences.models import GeneralOption
|
from preferences.models import GeneralOption
|
||||||
from re2o.acl import (
|
from re2o.acl import (can_create, can_delete, can_delete_set, can_edit,
|
||||||
can_create,
|
can_view, can_view_all)
|
||||||
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.base import SortTable, re2o_paginator
|
from re2o.base import SortTable, re2o_paginator
|
||||||
|
from re2o.utils import all_active_assigned_interfaces, filter_active_interfaces
|
||||||
from re2o.views import form
|
from re2o.views import form
|
||||||
from users.models import User
|
from users.models import User
|
||||||
from .forms import (
|
|
||||||
NewMachineForm,
|
from .forms import (AddInterfaceForm, AliasForm, DelAliasForm, DelDNameForm,
|
||||||
EditMachineForm,
|
DelExtensionForm, DelIpTypeForm, DelMachineTypeForm,
|
||||||
EditInterfaceForm,
|
DelMxForm, DelNasForm, DelNsForm, DelRoleForm,
|
||||||
AddInterfaceForm,
|
DelServiceForm, DelSOAForm, DelSrvForm, DelTxtForm,
|
||||||
MachineTypeForm,
|
DelVlanForm, DNameForm, DomainForm, EditInterfaceForm,
|
||||||
DelMachineTypeForm,
|
EditIpTypeForm, EditMachineForm,
|
||||||
ExtensionForm,
|
EditOuverturePortConfigForm, EditOuverturePortListForm,
|
||||||
DelExtensionForm,
|
ExtensionForm, IpTypeForm, Ipv6ListForm, MachineTypeForm,
|
||||||
EditIpTypeForm,
|
MxForm, NasForm, NewMachineForm, NsForm, RoleForm,
|
||||||
IpTypeForm,
|
ServiceForm, SOAForm, SrvForm, SshFpForm, TxtForm,
|
||||||
DelIpTypeForm,
|
VlanForm)
|
||||||
DomainForm,
|
from .models import (SOA, DName, Domain, Extension, Interface, IpType,
|
||||||
AliasForm,
|
Ipv6List, Machine, MachineType, Mx, Nas, Ns,
|
||||||
DelAliasForm,
|
OuverturePort, OuverturePortList, Role, Service,
|
||||||
SOAForm,
|
Service_link, Srv, SshFp, Txt, Vlan, regen)
|
||||||
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,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -135,7 +81,9 @@ def new_machine(request, user, **_kwargs):
|
||||||
"""
|
"""
|
||||||
machine = NewMachineForm(request.POST or None, user=request.user)
|
machine = NewMachineForm(request.POST or None, user=request.user)
|
||||||
interface = AddInterfaceForm(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():
|
if machine.is_valid() and interface.is_valid():
|
||||||
new_machine_obj = machine.save(commit=False)
|
new_machine_obj = machine.save(commit=False)
|
||||||
new_machine_obj.user = user
|
new_machine_obj.user = user
|
||||||
|
@ -229,7 +177,11 @@ def new_interface(request, machine, **_kwargs):
|
||||||
machine.
|
machine.
|
||||||
"""
|
"""
|
||||||
interface_form = AddInterfaceForm(request.POST or None, user=request.user)
|
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():
|
if interface_form.is_valid():
|
||||||
new_interface_obj = interface_form.save(commit=False)
|
new_interface_obj = interface_form.save(commit=False)
|
||||||
domain_form.instance.interface_parent = new_interface_obj
|
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)})
|
reverse("users:profil", kwargs={"userid": str(request.user.id)})
|
||||||
)
|
)
|
||||||
return form(
|
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)})
|
reverse("machines:index-ipv6", kwargs={"interfaceid": str(interfaceid)})
|
||||||
)
|
)
|
||||||
return form(
|
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)})
|
reverse("machines:index-sshfp", kwargs={"machineid": str(machineid)})
|
||||||
)
|
)
|
||||||
return form(
|
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()
|
iptype.save()
|
||||||
messages.success(request, _("The IP type was edited."))
|
messages.success(request, _("The IP type was edited."))
|
||||||
except IntegrityError as e:
|
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 redirect(reverse("machines:index-iptype"))
|
||||||
return form(
|
return form(
|
||||||
{"iptypeform": iptype, "action_name": _("Edit")},
|
{"iptypeform": iptype, "action_name": _("Edit")},
|
||||||
|
@ -490,7 +450,10 @@ def edit_machinetype(request, machinetype_instance, **_kwargs):
|
||||||
machinetype.save()
|
machinetype.save()
|
||||||
messages.success(request, _("The machine type was edited."))
|
messages.success(request, _("The machine type was edited."))
|
||||||
except IntegrityError as e:
|
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 redirect(reverse("machines:index-machinetype"))
|
||||||
return form(
|
return form(
|
||||||
{"machinetypeform": machinetype, "action_name": _("Edit")},
|
{"machinetypeform": machinetype, "action_name": _("Edit")},
|
||||||
|
@ -580,10 +543,16 @@ def del_extension(request, instances):
|
||||||
_(
|
_(
|
||||||
"The extension %s is assigned to following %s : %s"
|
"The extension %s is assigned to following %s : %s"
|
||||||
", you can't delete it."
|
", you can't delete it."
|
||||||
) % (
|
)
|
||||||
|
% (
|
||||||
extension_del,
|
extension_del,
|
||||||
str(e.protected_objects.model._meta.verbose_name_plural),
|
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,17 +1190,18 @@ def index(request):
|
||||||
machines_list = re2o_paginator(request, machines_list, pagination_large_number)
|
machines_list = re2o_paginator(request, machines_list, pagination_large_number)
|
||||||
return render(request, "machines/index.html", {"machines_list": machines_list})
|
return render(request, "machines/index.html", {"machines_list": machines_list})
|
||||||
|
|
||||||
|
|
||||||
# Canonic view for displaying machines in users's profil
|
# Canonic view for displaying machines in users's profil
|
||||||
def aff_profil(request, user):
|
def aff_profil(request, user):
|
||||||
"""View used to display the machines on a user's profile."""
|
"""View used to display the machines on a user's profile."""
|
||||||
machines = (
|
machines = (
|
||||||
Machine.objects.filter(user=user)
|
Machine.objects.filter(user=user)
|
||||||
.select_related("user")
|
.select_related("user")
|
||||||
.prefetch_related("interface_set__domain__extension")
|
.prefetch_related("interface_set__domain__extension")
|
||||||
.prefetch_related("interface_set__ipv4__ip_type__extension")
|
.prefetch_related("interface_set__ipv4__ip_type__extension")
|
||||||
.prefetch_related("interface_set__machine_type")
|
.prefetch_related("interface_set__machine_type")
|
||||||
.prefetch_related("interface_set__domain__related_domain__extension")
|
.prefetch_related("interface_set__domain__related_domain__extension")
|
||||||
)
|
)
|
||||||
machines = SortTable.sort(
|
machines = SortTable.sort(
|
||||||
machines,
|
machines,
|
||||||
request.GET.get("col"),
|
request.GET.get("col"),
|
||||||
|
@ -1243,19 +1213,16 @@ def aff_profil(request, user):
|
||||||
machines = re2o_paginator(request, machines, pagination_large_number)
|
machines = re2o_paginator(request, machines, pagination_large_number)
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"users":user,
|
"users": user,
|
||||||
"machines_list": machines,
|
"machines_list": machines,
|
||||||
"nb_machines":nb_machines,
|
"nb_machines": nb_machines,
|
||||||
}
|
}
|
||||||
|
|
||||||
return render_to_string(
|
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
|
@login_required
|
||||||
@can_view_all(IpType)
|
@can_view_all(IpType)
|
||||||
def index_iptype(request):
|
def index_iptype(request):
|
||||||
|
@ -1531,4 +1498,3 @@ def configure_ports(request, interface_instance, **_kwargs):
|
||||||
"machines/machine.html",
|
"machines/machine.html",
|
||||||
request,
|
request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -31,23 +31,14 @@ Here are defined the autocomplete class based view.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
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 django.db.models.functions import Concat
|
||||||
|
|
||||||
from .models import (
|
|
||||||
Interface,
|
|
||||||
Machine,
|
|
||||||
Vlan,
|
|
||||||
MachineType,
|
|
||||||
IpType,
|
|
||||||
Extension,
|
|
||||||
Domain,
|
|
||||||
OuverturePortList,
|
|
||||||
IpList,
|
|
||||||
)
|
|
||||||
|
|
||||||
from re2o.views import AutocompleteViewMixin
|
from re2o.views import AutocompleteViewMixin
|
||||||
|
|
||||||
|
from .models import (Domain, Extension, Interface, IpList, IpType, Machine,
|
||||||
|
MachineType, OuverturePortList, Vlan)
|
||||||
|
|
||||||
|
|
||||||
class VlanAutocomplete(AutocompleteViewMixin):
|
class VlanAutocomplete(AutocompleteViewMixin):
|
||||||
obj_type = Vlan
|
obj_type = Vlan
|
||||||
|
|
|
@ -27,11 +27,11 @@ Select a dorm
|
||||||
|
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.forms import ModelForm, Form
|
from django.forms import Form, ModelForm
|
||||||
from re2o.field_permissions import FieldPermissionFormMixin
|
|
||||||
from re2o.mixins import FormRevMixin
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
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 topologie.models import Dormitory
|
||||||
|
|
||||||
from .preferences.models import MultiopOption
|
from .preferences.models import MultiopOption
|
||||||
|
@ -49,4 +49,6 @@ class DormitoryForm(FormRevMixin, Form):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(DormitoryForm, self).__init__(*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()
|
||||||
|
|
|
@ -25,21 +25,18 @@ Multi_op model
|
||||||
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
from django.core.mail import EmailMessage
|
||||||
from django.db import models
|
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.db.models.signals import post_save
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
from django.template import loader
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from reversion.models import Version
|
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
|
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
|
from .preferences.models import MultiopOption
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
# quelques clics.
|
# quelques clics.
|
||||||
#
|
#
|
||||||
# Copyright © 2020 Gabriel Détraz
|
# Copyright © 2020 Gabriel Détraz
|
||||||
# Copyright © 2019 Arthur Grisel-Davy
|
# Copyright © 2019 Arthur Grisel-Davy
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -27,8 +27,9 @@ each.
|
||||||
|
|
||||||
|
|
||||||
from django import forms
|
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 django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from re2o.widgets import AutocompleteMultipleModelWidget
|
from re2o.widgets import AutocompleteMultipleModelWidget
|
||||||
|
|
||||||
from .models import MultiopOption
|
from .models import MultiopOption
|
||||||
|
|
|
@ -27,8 +27,8 @@ with multiple operators.
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from re2o.mixins import AclMixin, RevMixin
|
|
||||||
from preferences.models import PreferencesModel
|
from preferences.models import PreferencesModel
|
||||||
|
from re2o.mixins import AclMixin, RevMixin
|
||||||
|
|
||||||
|
|
||||||
class MultiopOption(AclMixin, PreferencesModel):
|
class MultiopOption(AclMixin, PreferencesModel):
|
||||||
|
|
|
@ -26,20 +26,16 @@
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
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.template.loader import render_to_string
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
from re2o.base import re2o_paginator
|
|
||||||
|
|
||||||
from re2o.acl import can_view, can_view_all, can_edit, can_create
|
|
||||||
|
|
||||||
from preferences.views import edit_options_template_function
|
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 forms, models
|
||||||
from . import models
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def aff_preferences(request):
|
def aff_preferences(request):
|
||||||
|
|
|
@ -27,28 +27,24 @@
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
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.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.views import form
|
||||||
from re2o.utils import all_has_access, all_adherent
|
from topologie.models import Dormitory, Room
|
||||||
|
|
||||||
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 .forms import DormitoryForm
|
from .forms import DormitoryForm
|
||||||
|
|
||||||
from .preferences.models import MultiopOption
|
from .preferences.models import MultiopOption
|
||||||
|
|
||||||
from topologie.models import Room, Dormitory
|
|
||||||
|
|
||||||
|
|
||||||
def display_rooms_connection(request, dormitory=None):
|
def display_rooms_connection(request, dormitory=None):
|
||||||
"""View used to display an overview of the rooms' connection state.
|
"""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: Dormitory, the dormitory used to filter rooms. If no
|
||||||
dormitory is given, all rooms are displayed (default: None).
|
dormitory is given, all rooms are displayed (default: None).
|
||||||
"""
|
"""
|
||||||
room_list = Room.objects.select_related("building__dormitory").filter(
|
room_list = (
|
||||||
building__dormitory__in=MultiopOption.get_cached_value("enabled_dorm").all()
|
Room.objects.select_related("building__dormitory")
|
||||||
).order_by("building_dormitory", "port")
|
.filter(
|
||||||
|
building__dormitory__in=MultiopOption.get_cached_value("enabled_dorm").all()
|
||||||
|
)
|
||||||
|
.order_by("building_dormitory", "port")
|
||||||
|
)
|
||||||
if dormitory:
|
if dormitory:
|
||||||
room_list = room_list.filter(building__dormitory=dormitory)
|
room_list = room_list.filter(building__dormitory=dormitory)
|
||||||
room_list = SortTable.sort(
|
room_list = SortTable.sort(
|
||||||
|
@ -113,7 +113,9 @@ def aff_pending_connection(request):
|
||||||
Room.objects.select_related("building__dormitory")
|
Room.objects.select_related("building__dormitory")
|
||||||
.filter(port__isnull=True)
|
.filter(port__isnull=True)
|
||||||
.filter(adherent__in=all_has_access())
|
.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")
|
.order_by("building_dormitory", "port")
|
||||||
)
|
)
|
||||||
dormitory_form = DormitoryForm(request.POST or None)
|
dormitory_form = DormitoryForm(request.POST or None)
|
||||||
|
@ -151,7 +153,9 @@ def aff_pending_disconnection(request):
|
||||||
Room.objects.select_related("building__dormitory")
|
Room.objects.select_related("building__dormitory")
|
||||||
.filter(port__isnull=False)
|
.filter(port__isnull=False)
|
||||||
.exclude(Q(adherent__in=all_has_access()) | Q(adherent__in=all_adherent()))
|
.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")
|
.order_by("building_dormitory", "port")
|
||||||
)
|
)
|
||||||
dormitory_form = DormitoryForm(request.POST or None)
|
dormitory_form = DormitoryForm(request.POST or None)
|
||||||
|
|
|
@ -28,21 +28,10 @@ from __future__ import unicode_literals
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from reversion.admin import VersionAdmin
|
from reversion.admin import VersionAdmin
|
||||||
|
|
||||||
from .models import (
|
from .models import (AssoOption, DocumentTemplate, GeneralOption, HomeOption,
|
||||||
OptionalUser,
|
MailContact, MailMessageOption, OptionalMachine,
|
||||||
OptionalMachine,
|
OptionalTopologie, OptionalUser, RadiusKey, Reminder,
|
||||||
OptionalTopologie,
|
Service, SwitchManagementCred)
|
||||||
GeneralOption,
|
|
||||||
Service,
|
|
||||||
MailContact,
|
|
||||||
AssoOption,
|
|
||||||
MailMessageOption,
|
|
||||||
HomeOption,
|
|
||||||
RadiusKey,
|
|
||||||
SwitchManagementCred,
|
|
||||||
Reminder,
|
|
||||||
DocumentTemplate,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class OptionalUserAdmin(VersionAdmin):
|
class OptionalUserAdmin(VersionAdmin):
|
||||||
|
|
|
@ -22,11 +22,12 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
import preferences.models as preferences
|
import preferences.models as preferences
|
||||||
from api.serializers import NamespacedHRField, NamespacedHIField, NamespacedHMSerializer
|
from api.serializers import (NamespacedHIField, NamespacedHMSerializer,
|
||||||
|
NamespacedHRField)
|
||||||
|
|
||||||
|
|
||||||
class OptionalUserSerializer(NamespacedHMSerializer):
|
class OptionalUserSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `preferences.models.OptionalUser` objects.
|
"""Serialize `preferences.models.OptionalUser` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
tel_mandatory = serializers.BooleanField(source="is_tel_mandatory")
|
tel_mandatory = serializers.BooleanField(source="is_tel_mandatory")
|
||||||
shell_default = serializers.StringRelatedField()
|
shell_default = serializers.StringRelatedField()
|
||||||
|
@ -47,8 +48,7 @@ class OptionalUserSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class OptionalMachineSerializer(NamespacedHMSerializer):
|
class OptionalMachineSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `preferences.models.OptionalMachine` objects.
|
"""Serialize `preferences.models.OptionalMachine` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = preferences.OptionalMachine
|
model = preferences.OptionalMachine
|
||||||
|
@ -59,13 +59,12 @@ class OptionalMachineSerializer(NamespacedHMSerializer):
|
||||||
"ipv6_mode",
|
"ipv6_mode",
|
||||||
"create_machine",
|
"create_machine",
|
||||||
"ipv6",
|
"ipv6",
|
||||||
"default_dns_ttl"
|
"default_dns_ttl",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class OptionalTopologieSerializer(NamespacedHMSerializer):
|
class OptionalTopologieSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `preferences.models.OptionalTopologie` objects.
|
"""Serialize `preferences.models.OptionalTopologie` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
switchs_management_interface_ip = serializers.CharField()
|
switchs_management_interface_ip = serializers.CharField()
|
||||||
|
|
||||||
|
@ -85,8 +84,7 @@ class OptionalTopologieSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class RadiusOptionSerializer(NamespacedHMSerializer):
|
class RadiusOptionSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `preferences.models.RadiusOption` objects
|
"""Serialize `preferences.models.RadiusOption` objects"""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = preferences.RadiusOption
|
model = preferences.RadiusOption
|
||||||
|
@ -107,8 +105,7 @@ class RadiusOptionSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class GeneralOptionSerializer(NamespacedHMSerializer):
|
class GeneralOptionSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `preferences.models.GeneralOption` objects.
|
"""Serialize `preferences.models.GeneralOption` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = preferences.GeneralOption
|
model = preferences.GeneralOption
|
||||||
|
@ -128,8 +125,7 @@ class GeneralOptionSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class HomeServiceSerializer(NamespacedHMSerializer):
|
class HomeServiceSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `preferences.models.Service` objects.
|
"""Serialize `preferences.models.Service` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = preferences.Service
|
model = preferences.Service
|
||||||
|
@ -138,8 +134,7 @@ class HomeServiceSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class AssoOptionSerializer(NamespacedHMSerializer):
|
class AssoOptionSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `preferences.models.AssoOption` objects.
|
"""Serialize `preferences.models.AssoOption` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = preferences.AssoOption
|
model = preferences.AssoOption
|
||||||
|
@ -157,8 +152,7 @@ class AssoOptionSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class HomeOptionSerializer(NamespacedHMSerializer):
|
class HomeOptionSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `preferences.models.HomeOption` objects.
|
"""Serialize `preferences.models.HomeOption` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = preferences.HomeOption
|
model = preferences.HomeOption
|
||||||
|
@ -166,8 +160,7 @@ class HomeOptionSerializer(NamespacedHMSerializer):
|
||||||
|
|
||||||
|
|
||||||
class MailMessageOptionSerializer(NamespacedHMSerializer):
|
class MailMessageOptionSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `preferences.models.MailMessageOption` objects.
|
"""Serialize `preferences.models.MailMessageOption` objects."""
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = preferences.MailMessageOption
|
model = preferences.MailMessageOption
|
||||||
|
|
|
@ -21,9 +21,7 @@
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
urls_viewset = [
|
urls_viewset = [(r"preferences/service", views.HomeServiceViewSet, "homeservice")]
|
||||||
(r"preferences/service", views.HomeServiceViewSet, "homeservice")
|
|
||||||
]
|
|
||||||
|
|
||||||
urls_view = [
|
urls_view = [
|
||||||
(r"preferences/optionaluser", views.OptionalUserView),
|
(r"preferences/optionaluser", views.OptionalUserView),
|
||||||
|
@ -33,5 +31,5 @@ urls_view = [
|
||||||
(r"preferences/generaloption", views.GeneralOptionView),
|
(r"preferences/generaloption", views.GeneralOptionView),
|
||||||
(r"preferences/assooption", views.AssoOptionView),
|
(r"preferences/assooption", views.AssoOptionView),
|
||||||
(r"preferences/homeoption", views.HomeOptionView),
|
(r"preferences/homeoption", views.HomeOptionView),
|
||||||
(r"preferences/mailmessageoption", views.MailMessageOptionView)
|
(r"preferences/mailmessageoption", views.MailMessageOptionView),
|
||||||
]
|
]
|
||||||
|
|
|
@ -19,16 +19,16 @@
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 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
|
import preferences.models as preferences
|
||||||
from api.permissions import ACLPermission
|
from api.permissions import ACLPermission
|
||||||
|
|
||||||
|
from . import serializers
|
||||||
|
|
||||||
|
|
||||||
class OptionalUserView(generics.RetrieveAPIView):
|
class OptionalUserView(generics.RetrieveAPIView):
|
||||||
"""Exposes details of `preferences.models.` settings.
|
"""Exposes details of `preferences.models.` settings."""
|
||||||
"""
|
|
||||||
|
|
||||||
permission_classes = (ACLPermission,)
|
permission_classes = (ACLPermission,)
|
||||||
perms_map = {"GET": [preferences.OptionalUser.can_view_all]}
|
perms_map = {"GET": [preferences.OptionalUser.can_view_all]}
|
||||||
|
@ -39,8 +39,7 @@ class OptionalUserView(generics.RetrieveAPIView):
|
||||||
|
|
||||||
|
|
||||||
class OptionalMachineView(generics.RetrieveAPIView):
|
class OptionalMachineView(generics.RetrieveAPIView):
|
||||||
"""Exposes details of `preferences.models.OptionalMachine` settings.
|
"""Exposes details of `preferences.models.OptionalMachine` settings."""
|
||||||
"""
|
|
||||||
|
|
||||||
permission_classes = (ACLPermission,)
|
permission_classes = (ACLPermission,)
|
||||||
perms_map = {"GET": [preferences.OptionalMachine.can_view_all]}
|
perms_map = {"GET": [preferences.OptionalMachine.can_view_all]}
|
||||||
|
@ -51,8 +50,7 @@ class OptionalMachineView(generics.RetrieveAPIView):
|
||||||
|
|
||||||
|
|
||||||
class OptionalTopologieView(generics.RetrieveAPIView):
|
class OptionalTopologieView(generics.RetrieveAPIView):
|
||||||
"""Exposes details of `preferences.models.OptionalTopologie` settings.
|
"""Exposes details of `preferences.models.OptionalTopologie` settings."""
|
||||||
"""
|
|
||||||
|
|
||||||
permission_classes = (ACLPermission,)
|
permission_classes = (ACLPermission,)
|
||||||
perms_map = {"GET": [preferences.OptionalTopologie.can_view_all]}
|
perms_map = {"GET": [preferences.OptionalTopologie.can_view_all]}
|
||||||
|
@ -63,8 +61,7 @@ class OptionalTopologieView(generics.RetrieveAPIView):
|
||||||
|
|
||||||
|
|
||||||
class RadiusOptionView(generics.RetrieveAPIView):
|
class RadiusOptionView(generics.RetrieveAPIView):
|
||||||
"""Exposes details of `preferences.models.OptionalTopologie` settings.
|
"""Exposes details of `preferences.models.OptionalTopologie` settings."""
|
||||||
"""
|
|
||||||
|
|
||||||
permission_classes = (ACLPermission,)
|
permission_classes = (ACLPermission,)
|
||||||
perms_map = {"GET": [preferences.RadiusOption.can_view_all]}
|
perms_map = {"GET": [preferences.RadiusOption.can_view_all]}
|
||||||
|
@ -75,8 +72,7 @@ class RadiusOptionView(generics.RetrieveAPIView):
|
||||||
|
|
||||||
|
|
||||||
class GeneralOptionView(generics.RetrieveAPIView):
|
class GeneralOptionView(generics.RetrieveAPIView):
|
||||||
"""Exposes details of `preferences.models.GeneralOption` settings.
|
"""Exposes details of `preferences.models.GeneralOption` settings."""
|
||||||
"""
|
|
||||||
|
|
||||||
permission_classes = (ACLPermission,)
|
permission_classes = (ACLPermission,)
|
||||||
perms_map = {"GET": [preferences.GeneralOption.can_view_all]}
|
perms_map = {"GET": [preferences.GeneralOption.can_view_all]}
|
||||||
|
@ -87,16 +83,14 @@ class GeneralOptionView(generics.RetrieveAPIView):
|
||||||
|
|
||||||
|
|
||||||
class HomeServiceViewSet(viewsets.ReadOnlyModelViewSet):
|
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()
|
queryset = preferences.Service.objects.all()
|
||||||
serializer_class = serializers.HomeServiceSerializer
|
serializer_class = serializers.HomeServiceSerializer
|
||||||
|
|
||||||
|
|
||||||
class AssoOptionView(generics.RetrieveAPIView):
|
class AssoOptionView(generics.RetrieveAPIView):
|
||||||
"""Exposes details of `preferences.models.AssoOption` settings.
|
"""Exposes details of `preferences.models.AssoOption` settings."""
|
||||||
"""
|
|
||||||
|
|
||||||
permission_classes = (ACLPermission,)
|
permission_classes = (ACLPermission,)
|
||||||
perms_map = {"GET": [preferences.AssoOption.can_view_all]}
|
perms_map = {"GET": [preferences.AssoOption.can_view_all]}
|
||||||
|
@ -107,8 +101,7 @@ class AssoOptionView(generics.RetrieveAPIView):
|
||||||
|
|
||||||
|
|
||||||
class HomeOptionView(generics.RetrieveAPIView):
|
class HomeOptionView(generics.RetrieveAPIView):
|
||||||
"""Exposes details of `preferences.models.HomeOption` settings.
|
"""Exposes details of `preferences.models.HomeOption` settings."""
|
||||||
"""
|
|
||||||
|
|
||||||
permission_classes = (ACLPermission,)
|
permission_classes = (ACLPermission,)
|
||||||
perms_map = {"GET": [preferences.HomeOption.can_view_all]}
|
perms_map = {"GET": [preferences.HomeOption.can_view_all]}
|
||||||
|
@ -119,12 +112,11 @@ class HomeOptionView(generics.RetrieveAPIView):
|
||||||
|
|
||||||
|
|
||||||
class MailMessageOptionView(generics.RetrieveAPIView):
|
class MailMessageOptionView(generics.RetrieveAPIView):
|
||||||
"""Exposes details of `preferences.models.MailMessageOption` settings.
|
"""Exposes details of `preferences.models.MailMessageOption` settings."""
|
||||||
"""
|
|
||||||
|
|
||||||
permission_classes = (ACLPermission,)
|
permission_classes = (ACLPermission,)
|
||||||
perms_map = {"GET": [preferences.MailMessageOption.can_view_all]}
|
perms_map = {"GET": [preferences.MailMessageOption.can_view_all]}
|
||||||
serializer_class = serializers.MailMessageOptionSerializer
|
serializer_class = serializers.MailMessageOptionSerializer
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
return preferences.MailMessageOption.objects.first()
|
return preferences.MailMessageOption.objects.first()
|
||||||
|
|
|
@ -8,4 +8,4 @@ from django.apps import AppConfig
|
||||||
class PreferencesConfig(AppConfig):
|
class PreferencesConfig(AppConfig):
|
||||||
"""Configuration of preferences app."""
|
"""Configuration of preferences app."""
|
||||||
|
|
||||||
name = "preferences"
|
name = "preferences"
|
||||||
|
|
|
@ -25,36 +25,22 @@ Forms to edit preferences: users, machines, topology, organisation etc.
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.forms import ModelForm, Form
|
|
||||||
from django.db.models import Q
|
|
||||||
from django import forms
|
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 django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from re2o.mixins import FormRevMixin
|
from re2o.mixins import FormRevMixin
|
||||||
from re2o.widgets import (
|
from re2o.widgets import (AutocompleteModelWidget,
|
||||||
AutocompleteModelWidget,
|
AutocompleteMultipleModelWidget)
|
||||||
AutocompleteMultipleModelWidget
|
|
||||||
)
|
|
||||||
from .models import (
|
|
||||||
OptionalUser,
|
|
||||||
OptionalMachine,
|
|
||||||
OptionalTopologie,
|
|
||||||
GeneralOption,
|
|
||||||
AssoOption,
|
|
||||||
MailMessageOption,
|
|
||||||
HomeOption,
|
|
||||||
Service,
|
|
||||||
MailContact,
|
|
||||||
Reminder,
|
|
||||||
RadiusKey,
|
|
||||||
SwitchManagementCred,
|
|
||||||
RadiusOption,
|
|
||||||
CotisationsOption,
|
|
||||||
DocumentTemplate,
|
|
||||||
RadiusAttribute,
|
|
||||||
Mandate,
|
|
||||||
)
|
|
||||||
from topologie.models import Switch
|
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):
|
class EditOptionalUserForm(ModelForm):
|
||||||
"""Form used to edit user preferences."""
|
"""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_shell"].label = _("Self change shell")
|
||||||
self.fields["self_change_pseudo"].label = _("Self change pseudo")
|
self.fields["self_change_pseudo"].label = _("Self change pseudo")
|
||||||
self.fields["self_room_policy"].label = _("Self room policy")
|
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["local_email_domain"].label = _("Local email domain")
|
||||||
self.fields["max_email_address"].label = _("Max local email address")
|
self.fields["max_email_address"].label = _("Max local email address")
|
||||||
self.fields["delete_notyetactive"].label = _("Delete not yet active users")
|
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["self_adhesion"].label = _("Self registration")
|
||||||
self.fields["all_users_active"].label = _("All users are state active by default")
|
self.fields["all_users_active"].label = _(
|
||||||
self.fields["allow_set_password_during_user_creation"].label = _("Allow set password during user creation")
|
"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")
|
self.fields["allow_archived_connexion"].label = _("Allow archived connexion")
|
||||||
|
|
||||||
|
|
||||||
|
@ -211,9 +205,7 @@ class EditMailMessageOptionForm(ModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||||
super(EditMailMessageOptionForm, self).__init__(*args, prefix=prefix, **kwargs)
|
super(EditMailMessageOptionForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||||
self.fields["welcome_mail_fr"].label = _(
|
self.fields["welcome_mail_fr"].label = _("Message for the French welcome email")
|
||||||
"Message for the French welcome email"
|
|
||||||
)
|
|
||||||
self.fields["welcome_mail_en"].label = _(
|
self.fields["welcome_mail_en"].label = _(
|
||||||
"Message for the English welcome email"
|
"Message for the English welcome email"
|
||||||
)
|
)
|
||||||
|
@ -355,8 +347,7 @@ class ServiceForm(ModelForm):
|
||||||
|
|
||||||
|
|
||||||
class DelServiceForm(Form):
|
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(
|
services = forms.ModelMultipleChoiceField(
|
||||||
queryset=Service.objects.none(),
|
queryset=Service.objects.none(),
|
||||||
|
|
|
@ -25,23 +25,22 @@ Models defining the preferences for users, machines, emails, general settings
|
||||||
etc.
|
etc.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import os
|
|
||||||
|
|
||||||
from django.utils.functional import cached_property
|
import os
|
||||||
from django.utils import timezone
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from django.core.cache import cache
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.core.cache import cache
|
|
||||||
from django.forms import ValidationError
|
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 _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
import machines.models
|
import machines.models
|
||||||
|
|
||||||
from re2o.mixins import AclMixin, RevMixin
|
|
||||||
from re2o.aes_field import AESEncryptedField
|
from re2o.aes_field import AESEncryptedField
|
||||||
|
from re2o.mixins import AclMixin, RevMixin
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
|
|
||||||
class PreferencesModel(models.Model):
|
class PreferencesModel(models.Model):
|
||||||
|
@ -328,7 +327,7 @@ class OptionalTopologie(AclMixin, PreferencesModel):
|
||||||
configuration.
|
configuration.
|
||||||
"""
|
"""
|
||||||
if self.switchs_ip_type:
|
if self.switchs_ip_type:
|
||||||
from machines.models import Role, Interface
|
from machines.models import Interface, Role
|
||||||
|
|
||||||
return (
|
return (
|
||||||
Interface.objects.filter(
|
Interface.objects.filter(
|
||||||
|
@ -364,14 +363,14 @@ class OptionalTopologie(AclMixin, PreferencesModel):
|
||||||
"""Get the dictionary of IP addresses for the configuration of
|
"""Get the dictionary of IP addresses for the configuration of
|
||||||
switches.
|
switches.
|
||||||
"""
|
"""
|
||||||
from machines.models import Role, Ipv6List, Interface
|
from machines.models import Interface, Ipv6List, Role
|
||||||
|
|
||||||
def return_ips_dict(interfaces):
|
def return_ips_dict(interfaces):
|
||||||
return {
|
return {
|
||||||
"ipv4": [str(interface.ipv4) for interface in interfaces],
|
"ipv4": [str(interface.ipv4) for interface in interfaces],
|
||||||
"ipv6": Ipv6List.objects.filter(interface__in=interfaces).filter(active=True).values_list(
|
"ipv6": Ipv6List.objects.filter(interface__in=interfaces)
|
||||||
"ipv6", flat=True
|
.filter(active=True)
|
||||||
),
|
.values_list("ipv6", flat=True),
|
||||||
}
|
}
|
||||||
|
|
||||||
ntp_servers = Role.all_interfaces_for_roletype("ntp-server").filter(
|
ntp_servers = Role.all_interfaces_for_roletype("ntp-server").filter(
|
||||||
|
@ -660,7 +659,7 @@ class Mandate(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_mandate(cls, date=timezone.now):
|
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:
|
Args:
|
||||||
date: the date used to find the mandate (default: timezone.now).
|
date: the date used to find the mandate (default: timezone.now).
|
||||||
|
|
|
@ -79,9 +79,7 @@ urlpatterns = [
|
||||||
name="edit-options",
|
name="edit-options",
|
||||||
),
|
),
|
||||||
path("add_service", views.add_service, name="add-service"),
|
path("add_service", views.add_service, name="add-service"),
|
||||||
path(
|
path("edit_service/<int:serviceid>", views.edit_service, name="edit-service"),
|
||||||
"edit_service/<int:serviceid>", views.edit_service, name="edit-service"
|
|
||||||
),
|
|
||||||
path("del_service/<int:serviceid>", views.del_service, name="del-service"),
|
path("del_service/<int:serviceid>", views.del_service, name="del-service"),
|
||||||
path("add_mailcontact", views.add_mailcontact, name="add-mailcontact"),
|
path("add_mailcontact", views.add_mailcontact, name="add-mailcontact"),
|
||||||
path(
|
path(
|
||||||
|
@ -143,13 +141,9 @@ urlpatterns = [
|
||||||
name="del-document-template",
|
name="del-document-template",
|
||||||
),
|
),
|
||||||
path("add_mandate", views.add_mandate, name="add-mandate"),
|
path("add_mandate", views.add_mandate, name="add-mandate"),
|
||||||
path(
|
path("edit_mandate/<int:mandateid>", views.edit_mandate, name="edit-mandate"),
|
||||||
"edit_mandate/<int:mandateid>", views.edit_mandate, name="edit-mandate"
|
|
||||||
),
|
|
||||||
path("del_mandate/<int:mandateid>", views.del_mandate, name="del-mandate"),
|
path("del_mandate/<int:mandateid>", views.del_mandate, name="del-mandate"),
|
||||||
path(
|
path("add_radiusattribute", views.add_radiusattribute, name="add-radiusattribute"),
|
||||||
"add_radiusattribute", views.add_radiusattribute, name="add-radiusattribute"
|
|
||||||
),
|
|
||||||
path(
|
path(
|
||||||
"edit_radiusattribute/<int:radiusattributeid>",
|
"edit_radiusattribute/<int:radiusattributeid>",
|
||||||
views.edit_radiusattribute,
|
views.edit_radiusattribute,
|
||||||
|
|
|
@ -30,61 +30,33 @@ services etc.)
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.urls import reverse
|
from importlib import import_module
|
||||||
from django.shortcuts import redirect
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.db.models import ProtectedError
|
|
||||||
from django.db import transaction
|
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 django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from reversion import revisions as reversion
|
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.settings_local import OPTIONNAL_APPS_RE2O
|
||||||
from re2o.views import form
|
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 . import forms, models
|
||||||
from .forms import (
|
from .forms import (DelDocumentTemplateForm, DelMailContactForm,
|
||||||
ServiceForm,
|
DelRadiusAttributeForm, DocumentTemplateForm,
|
||||||
ReminderForm,
|
MailContactForm, MandateForm, RadiusAttributeForm,
|
||||||
RadiusKeyForm,
|
RadiusKeyForm, ReminderForm, ServiceForm,
|
||||||
SwitchManagementCredForm,
|
SwitchManagementCredForm)
|
||||||
DocumentTemplateForm,
|
from .models import (AssoOption, CotisationsOption, DocumentTemplate,
|
||||||
DelDocumentTemplateForm,
|
GeneralOption, HomeOption, MailContact, MailMessageOption,
|
||||||
RadiusAttributeForm,
|
Mandate, OptionalMachine, OptionalTopologie, OptionalUser,
|
||||||
DelRadiusAttributeForm,
|
RadiusAttribute, RadiusKey, RadiusOption, Reminder,
|
||||||
MandateForm,
|
Service, SwitchManagementCred)
|
||||||
)
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def edit_options_template_function(request, section, forms, models):
|
def edit_options_template_function(request, section, forms, models):
|
||||||
|
|
|
@ -30,8 +30,8 @@ from __future__ import unicode_literals
|
||||||
import sys
|
import sys
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
from django.db.models import Model
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
from django.db.models import Model
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
|
@ -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
|
to handle the use of properly encrypting and decrypting AES keys
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import string
|
|
||||||
import binascii
|
import binascii
|
||||||
|
import string
|
||||||
from random import choice
|
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 import forms
|
||||||
from django.conf import settings
|
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_asbyte = b"`%EofD%`" # This should be something that will not occur in strings
|
||||||
EOD = EOD_asbyte.decode("utf-8")
|
EOD = EOD_asbyte.decode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
def genstring(length=16, chars=string.printable):
|
def genstring(length=16, chars=string.printable):
|
||||||
""" Generate a random string of length `length` and composed of
|
"""Generate a random string of length `length` and composed of
|
||||||
the characters in `chars` """
|
the characters in `chars`"""
|
||||||
return "".join([choice(chars) for i in range(length)])
|
return "".join([choice(chars) for i in range(length)])
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,8 +71,8 @@ class AESEncryptedFormField(forms.CharField):
|
||||||
|
|
||||||
|
|
||||||
class AESEncryptedField(models.CharField):
|
class AESEncryptedField(models.CharField):
|
||||||
""" A Field that can be used in forms for adding the support
|
"""A Field that can be used in forms for adding the support
|
||||||
of AES ecnrypted fields """
|
of AES ecnrypted fields"""
|
||||||
|
|
||||||
def save_form_data(self, instance, data):
|
def save_form_data(self, instance, data):
|
||||||
setattr(
|
setattr(
|
||||||
|
|
|
@ -8,4 +8,4 @@ from django.apps import AppConfig
|
||||||
class Re2oConfig(AppConfig):
|
class Re2oConfig(AppConfig):
|
||||||
"""Configuration of re2o app."""
|
"""Configuration of re2o app."""
|
||||||
|
|
||||||
name = "re2o"
|
name = "re2o"
|
||||||
|
|
13
re2o/base.py
13
re2o/base.py
|
@ -26,12 +26,11 @@ Global independant usefull functions
|
||||||
|
|
||||||
import smtplib
|
import smtplib
|
||||||
|
|
||||||
|
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
|
||||||
|
|
||||||
from re2o.settings import EMAIL_HOST
|
from re2o.settings import EMAIL_HOST
|
||||||
|
|
||||||
|
|
||||||
# Mapping of srtftime format for better understanding
|
# Mapping of srtftime format for better understanding
|
||||||
# https://docs.python.org/3.6/library/datetime.html#strftime-strptime-behavior
|
# https://docs.python.org/3.6/library/datetime.html#strftime-strptime-behavior
|
||||||
datetime_mapping = {
|
datetime_mapping = {
|
||||||
|
@ -64,7 +63,7 @@ datetime_mapping = {
|
||||||
|
|
||||||
def smtp_check(local_part):
|
def smtp_check(local_part):
|
||||||
"""Return True if the local_part is already taken
|
"""Return True if the local_part is already taken
|
||||||
False if available"""
|
False if available"""
|
||||||
try:
|
try:
|
||||||
srv = smtplib.SMTP(EMAIL_HOST)
|
srv = smtplib.SMTP(EMAIL_HOST)
|
||||||
srv.putcmd("vrfy", local_part)
|
srv.putcmd("vrfy", local_part)
|
||||||
|
@ -108,9 +107,9 @@ def get_input_formats_help_text(input_formats):
|
||||||
|
|
||||||
|
|
||||||
class SortTable:
|
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
|
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
|
# All the possible possible values
|
||||||
# The naming convention is based on the URL or the views function
|
# The naming convention is based on the URL or the views function
|
||||||
|
@ -228,8 +227,8 @@ class SortTable:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def sort(request, col, order, values):
|
def sort(request, col, order, values):
|
||||||
""" Check if the given values are possible and add .order_by() and
|
"""Check if the given values are possible and add .order_by() and
|
||||||
a .reverse() as specified according to those values """
|
a .reverse() as specified according to those values"""
|
||||||
fields = values.get(col, None)
|
fields = values.get(col, None)
|
||||||
if not fields:
|
if not fields:
|
||||||
fields = values.get("default", [])
|
fields = values.get("default", [])
|
||||||
|
|
|
@ -24,21 +24,22 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
from importlib import import_module
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.messages import get_messages
|
from django.contrib.messages import get_messages
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
from preferences.models import GeneralOption, OptionalMachine
|
|
||||||
from django.utils.translation import get_language
|
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
|
from re2o.settings_local import OPTIONNAL_APPS_RE2O
|
||||||
|
|
||||||
|
|
||||||
def context_user(request):
|
def context_user(request):
|
||||||
"""Global Context function
|
"""Global Context function
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
dict:Containing user's interfaces and himself if logged, else None
|
dict:Containing user's interfaces and himself if logged, else None
|
||||||
|
|
||||||
"""
|
"""
|
||||||
user = request.user
|
user = request.user
|
||||||
|
@ -51,7 +52,9 @@ def context_user(request):
|
||||||
if global_message not in [msg.message for msg in get_messages(request)]:
|
if global_message not in [msg.message for msg in get_messages(request)]:
|
||||||
messages.warning(request, global_message)
|
messages.warning(request, global_message)
|
||||||
else:
|
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)
|
messages.warning(request._request, global_message)
|
||||||
if user.is_authenticated:
|
if user.is_authenticated:
|
||||||
interfaces = user.user_interfaces()
|
interfaces = user.user_interfaces()
|
||||||
|
@ -70,9 +73,9 @@ def context_user(request):
|
||||||
def context_optionnal_apps(request):
|
def context_optionnal_apps(request):
|
||||||
"""Context functions. Called to add optionnal apps buttons in navbari
|
"""Context functions. Called to add optionnal apps buttons in navbari
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
dict:Containing optionnal template list of functions for navbar found
|
dict:Containing optionnal template list of functions for navbar found
|
||||||
in optional apps
|
in optional apps
|
||||||
|
|
||||||
"""
|
"""
|
||||||
optionnal_apps = [import_module(app) for app in OPTIONNAL_APPS_RE2O]
|
optionnal_apps = [import_module(app) for app in OPTIONNAL_APPS_RE2O]
|
||||||
|
|
|
@ -4,44 +4,44 @@ A list of the proud contributors to Re2o
|
||||||
"""
|
"""
|
||||||
|
|
||||||
CONTRIBUTORS = [
|
CONTRIBUTORS = [
|
||||||
'Gabriel Detraz',
|
"Gabriel Detraz",
|
||||||
'Hugo Levy-falk',
|
"Hugo Levy-falk",
|
||||||
'Maël Kervella',
|
"Maël Kervella",
|
||||||
'Jean-romain Garnier',
|
"Jean-romain Garnier",
|
||||||
'Arthur Grisel-davy',
|
"Arthur Grisel-davy",
|
||||||
'Laouen Fernet',
|
"Laouen Fernet",
|
||||||
'Augustin Lemesle',
|
"Augustin Lemesle",
|
||||||
'Lara Kermarec',
|
"Lara Kermarec",
|
||||||
'Root `root` Root',
|
"Root `root` Root",
|
||||||
'Alexandre Iooss',
|
"Alexandre Iooss",
|
||||||
'Yoann Piétri',
|
"Yoann Piétri",
|
||||||
'Charlie Jacomme',
|
"Charlie Jacomme",
|
||||||
'Corentin Canebier',
|
"Corentin Canebier",
|
||||||
'Bombar Maxime',
|
"Bombar Maxime",
|
||||||
'Guillaume Goessel',
|
"Guillaume Goessel",
|
||||||
'Matthieu Michelet',
|
"Matthieu Michelet",
|
||||||
'Edpibu',
|
"Edpibu",
|
||||||
'Fardale',
|
"Fardale",
|
||||||
'Jean-marie Mineau',
|
"Jean-marie Mineau",
|
||||||
'David Sinquin',
|
"David Sinquin",
|
||||||
'Gabriel Le Bouder',
|
"Gabriel Le Bouder",
|
||||||
'Simon Brélivet',
|
"Simon Brélivet",
|
||||||
'~anonymised~',
|
"~anonymised~",
|
||||||
'Benjamin Graillot',
|
"Benjamin Graillot",
|
||||||
'Leïla Bekaddour',
|
"Leïla Bekaddour",
|
||||||
'Éloi Alain',
|
"Éloi Alain",
|
||||||
'Pierre Cadart',
|
"Pierre Cadart",
|
||||||
'Antoine Vintache',
|
"Antoine Vintache",
|
||||||
'Thibault De Boutray',
|
"Thibault De Boutray",
|
||||||
'Delphine Salvy',
|
"Delphine Salvy",
|
||||||
'Joanne Steiner',
|
"Joanne Steiner",
|
||||||
'Krokmou',
|
"Krokmou",
|
||||||
'B',
|
"B",
|
||||||
'Daniel Stan',
|
"Daniel Stan",
|
||||||
'Gwenael Le Hir',
|
"Gwenael Le Hir",
|
||||||
'Hugo Hervieux',
|
"Hugo Hervieux",
|
||||||
'Mikachu',
|
"Mikachu",
|
||||||
'Nymous',
|
"Nymous",
|
||||||
'Pierre-antoine Comby',
|
"Pierre-antoine Comby",
|
||||||
'Vincent Le Gallic',
|
"Vincent Le Gallic",
|
||||||
]
|
]
|
||||||
|
|
|
@ -40,8 +40,8 @@ class FieldPermissionModelMixin:
|
||||||
FIELD_PERMISSION_MISSING_DEFAULT = True
|
FIELD_PERMISSION_MISSING_DEFAULT = True
|
||||||
|
|
||||||
def has_field_perm(self, user, field):
|
def has_field_perm(self, user, field):
|
||||||
""" Checks if a `user` has the right to edit the `field`
|
"""Checks if a `user` has the right to edit the `field`
|
||||||
of this model """
|
of this model"""
|
||||||
if field in self.field_permissions:
|
if field in self.field_permissions:
|
||||||
checks = self.field_permissions[field]
|
checks = self.field_permissions[field]
|
||||||
if not isinstance(checks, (list, tuple)):
|
if not isinstance(checks, (list, tuple)):
|
||||||
|
|
|
@ -32,12 +32,12 @@ import binascii
|
||||||
import crypt
|
import crypt
|
||||||
import hashlib
|
import hashlib
|
||||||
import os
|
import os
|
||||||
from base64 import encodestring, decodestring, b64encode, b64decode
|
from base64 import b64decode, b64encode, decodestring, encodestring
|
||||||
from collections import OrderedDict
|
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 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_NAME = "{SSHA}"
|
||||||
ALGO_LEN = len(ALGO_NAME + "$")
|
ALGO_LEN = len(ALGO_NAME + "$")
|
||||||
|
@ -45,7 +45,7 @@ DIGEST_LEN = 20
|
||||||
|
|
||||||
|
|
||||||
def makeSecret(password):
|
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:
|
Parameters:
|
||||||
password (string): Password to hash
|
password (string): Password to hash
|
||||||
|
@ -60,7 +60,7 @@ def makeSecret(password):
|
||||||
|
|
||||||
|
|
||||||
def hashNT(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:
|
Parameters:
|
||||||
password (string): Password to hash
|
password (string): Password to hash
|
||||||
|
@ -78,7 +78,7 @@ def checkPassword(challenge_password, password):
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
challenge_password (string): Password to verify with hash
|
challenge_password (string): Password to verify with hash
|
||||||
password (string): Hashed password to verify
|
password (string): Hashed password to verify
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
boolean: True if challenge_password and password match
|
boolean: True if challenge_password and password match
|
||||||
|
@ -93,7 +93,7 @@ def checkPassword(challenge_password, password):
|
||||||
|
|
||||||
|
|
||||||
def hash_password_salt(hashed_password):
|
def hash_password_salt(hashed_password):
|
||||||
""" Extract the salt from a given hashed password
|
"""Extract the salt from a given hashed password
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
hashed_password (string): Hashed password to extract salt
|
hashed_password (string): Hashed password to extract salt
|
||||||
|
@ -277,15 +277,17 @@ class SSHAPasswordHasher(hashers.BasePasswordHasher):
|
||||||
class RecryptBackend(ModelBackend):
|
class RecryptBackend(ModelBackend):
|
||||||
"""Function for legacy users. During auth, if their hash password is different from SSHA or ntlm
|
"""Function for legacy users. During auth, if their hash password is different from SSHA or ntlm
|
||||||
password is empty, rehash in SSHA or NTLM
|
password is empty, rehash in SSHA or NTLM
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
model user instance: Instance of the user logged
|
model user instance: Instance of the user logged
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def authenticate(self, request, username=None, password=None, **kwargs):
|
def authenticate(self, request, username=None, password=None, **kwargs):
|
||||||
# we obtain from the classical auth backend the user
|
# 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 user:
|
||||||
if not (user.pwd_ntlm):
|
if not (user.pwd_ntlm):
|
||||||
# if we dont have NT hash, we create it
|
# if we dont have NT hash, we create it
|
||||||
|
|
|
@ -25,11 +25,13 @@
|
||||||
All functions linked with emails here. Non model or app dependant
|
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 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):
|
def send_mail(request, *args, **kwargs):
|
||||||
"""Wrapper for Django's send_mail which handles errors"""
|
"""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:
|
except (SMTPException, ConnectionError, herror, gaierror) as e:
|
||||||
messages.error(
|
messages.error(
|
||||||
request,
|
request,
|
||||||
_("Failed to send email: %(error)s.") % {
|
_("Failed to send email: %(error)s.")
|
||||||
|
% {
|
||||||
"error": e,
|
"error": e,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -53,7 +56,8 @@ def send_mail_object(mail, request):
|
||||||
if request:
|
if request:
|
||||||
messages.error(
|
messages.error(
|
||||||
request,
|
request,
|
||||||
_("Failed to send email: %(error)s.") % {
|
_("Failed to send email: %(error)s.")
|
||||||
|
% {
|
||||||
"error": e,
|
"error": e,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -25,6 +25,7 @@ commits. This list is extracted from the current gitlab repository.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,9 +23,9 @@
|
||||||
A set of mixins used all over the project to avoid duplicating code
|
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.db import transaction
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
from reversion import revisions as reversion
|
||||||
|
|
||||||
|
|
||||||
class RevMixin(object):
|
class RevMixin(object):
|
||||||
|
@ -252,4 +252,3 @@ class AclMixin(object):
|
||||||
else None,
|
else None,
|
||||||
(permission,),
|
(permission,),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -24,17 +24,16 @@ with Re2o throught the CLI
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from os.path import dirname
|
|
||||||
import sys
|
|
||||||
import pwd
|
import pwd
|
||||||
|
import sys
|
||||||
from getpass import getpass
|
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.management.base import CommandError
|
||||||
|
from django.core.wsgi import get_wsgi_application
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.utils.html import strip_tags
|
from django.utils.html import strip_tags
|
||||||
|
from reversion import revisions as reversion
|
||||||
|
|
||||||
from users.models import User
|
from users.models import User
|
||||||
|
|
||||||
|
@ -48,27 +47,24 @@ application = get_wsgi_application()
|
||||||
|
|
||||||
def get_user(pseudo):
|
def get_user(pseudo):
|
||||||
"""Find a user from its pseudo
|
"""Find a user from its pseudo
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
pseudo (string): pseudo of this user
|
pseudo (string): pseudo of this user
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
user instance:Instance of user
|
user instance:Instance of user
|
||||||
|
|
||||||
"""
|
"""
|
||||||
user = User.objects.filter(pseudo=pseudo)
|
user = User.objects.filter(pseudo=pseudo)
|
||||||
if len(user) == 0:
|
if len(user) == 0:
|
||||||
raise CommandError("Invalid user.")
|
raise CommandError("Invalid user.")
|
||||||
if len(user) > 1:
|
if len(user) > 1:
|
||||||
raise CommandError(
|
raise CommandError("Several users match this username. This SHOULD NOT happen.")
|
||||||
"Several users match this username. This SHOULD NOT happen."
|
|
||||||
)
|
|
||||||
return user[0]
|
return user[0]
|
||||||
|
|
||||||
|
|
||||||
def get_system_user():
|
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
|
return pwd.getpwuid(int(os.getenv("SUDO_UID") or os.getuid())).pw_name
|
||||||
|
|
||||||
|
|
||||||
|
@ -80,7 +76,7 @@ def form_cli(Form, user, action, *args, **kwargs):
|
||||||
Form : a django class form to fill-in
|
Form : a django class form to fill-in
|
||||||
user : a re2o user doign the modification
|
user : a re2o user doign the modification
|
||||||
action: the action done with that form, for logs purpose
|
action: the action done with that form, for logs purpose
|
||||||
|
|
||||||
"""
|
"""
|
||||||
data = {}
|
data = {}
|
||||||
dumb_form = Form(user=user, *args, **kwargs)
|
dumb_form = Form(user=user, *args, **kwargs)
|
||||||
|
@ -105,6 +101,4 @@ def form_cli(Form, user, action, *args, **kwargs):
|
||||||
reversion.set_user(user)
|
reversion.set_user(user)
|
||||||
reversion.set_comment(action)
|
reversion.set_comment(action)
|
||||||
|
|
||||||
sys.stdout.write(
|
sys.stdout.write("%s: done. The edit may take several minutes to apply.\n" % action)
|
||||||
"%s: done. The edit may take several minutes to apply.\n" % action
|
|
||||||
)
|
|
||||||
|
|
|
@ -36,7 +36,9 @@ https://docs.djangoproject.com/en/1.8/ref/settings/
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from .settings_default import *
|
from .settings_default import *
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from .settings_local import *
|
from .settings_local import *
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -60,7 +62,11 @@ LOGIN_REDIRECT_URL = "/" # The URL for redirecting after login
|
||||||
|
|
||||||
# Application definition
|
# 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)
|
# 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_APPS = (
|
||||||
"django.contrib.admin",
|
"django.contrib.admin",
|
||||||
"django.contrib.auth",
|
"django.contrib.auth",
|
||||||
|
@ -82,7 +88,11 @@ LOCAL_APPS = (
|
||||||
"logs",
|
"logs",
|
||||||
)
|
)
|
||||||
INSTALLED_APPS = (
|
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 = (
|
MIDDLEWARE = (
|
||||||
"django.middleware.security.SecurityMiddleware",
|
"django.middleware.security.SecurityMiddleware",
|
||||||
|
@ -196,18 +206,18 @@ EMAIL_TIMEOUT = 10
|
||||||
|
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
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",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
# Activate API
|
# Activate API
|
||||||
if "api" in INSTALLED_APPS:
|
if "api" in INSTALLED_APPS:
|
||||||
|
|
|
@ -56,7 +56,7 @@ GID_RANGES = {"posix": [501, 600]}
|
||||||
|
|
||||||
# If you want to add a database routers, please fill in above and add your databse.
|
# If you want to add a database routers, please fill in above and add your databse.
|
||||||
# Then, add a file "local_routers.py" in folder app re2o, and add your router path in
|
# Then, add a file "local_routers.py" in folder app re2o, and add your router path in
|
||||||
# the LOCAL_ROUTERS var as "re2o.local_routers.DbRouter". You can also add extra routers.
|
# the LOCAL_ROUTERS var as "re2o.local_routers.DbRouter". You can also add extra routers.
|
||||||
LOCAL_ROUTERS = []
|
LOCAL_ROUTERS = []
|
||||||
|
|
||||||
# Some optionnal Re2o Apps
|
# Some optionnal Re2o Apps
|
||||||
|
@ -65,24 +65,24 @@ OPTIONNAL_APPS_RE2O = ()
|
||||||
# Some Django apps you want to add in you local project
|
# Some Django apps you want to add in you local project
|
||||||
OPTIONNAL_APPS = OPTIONNAL_APPS_RE2O + ()
|
OPTIONNAL_APPS = OPTIONNAL_APPS_RE2O + ()
|
||||||
|
|
||||||
#Set auth password validator
|
# Set auth password validator
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
{
|
{
|
||||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||||
'OPTIONS': {
|
"OPTIONS": {
|
||||||
'user_attributes': ['surname', 'pseudo', 'name', 'email'],
|
"user_attributes": ["surname", "pseudo", "name", "email"],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||||
'OPTIONS': {
|
"OPTIONS": {
|
||||||
'min_length': 8,
|
"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",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
@ -105,7 +105,7 @@ GID_RANGES = {"posix": [501, 600]}
|
||||||
|
|
||||||
# If you want to add a database routers, please fill in above and add your databse.
|
# If you want to add a database routers, please fill in above and add your databse.
|
||||||
# Then, add a file "local_routers.py" in folder app re2o, and add your router path in
|
# Then, add a file "local_routers.py" in folder app re2o, and add your router path in
|
||||||
# the LOCAL_ROUTERS var as "re2o.local_routers.DbRouter". You can also add extra routers.
|
# the LOCAL_ROUTERS var as "re2o.local_routers.DbRouter". You can also add extra routers.
|
||||||
LOCAL_ROUTERS = []
|
LOCAL_ROUTERS = []
|
||||||
|
|
||||||
# Some optionnal Re2o Apps
|
# Some optionnal Re2o Apps
|
||||||
|
@ -114,24 +114,24 @@ OPTIONNAL_APPS_RE2O = ()
|
||||||
# Some Django apps you want to add in you local project
|
# Some Django apps you want to add in you local project
|
||||||
OPTIONNAL_APPS = OPTIONNAL_APPS_RE2O + ()
|
OPTIONNAL_APPS = OPTIONNAL_APPS_RE2O + ()
|
||||||
|
|
||||||
#Set auth password validator
|
# Set auth password validator
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
{
|
{
|
||||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||||
'OPTIONS': {
|
"OPTIONS": {
|
||||||
'user_attributes': ['surname', 'pseudo', 'name', 'email'],
|
"user_attributes": ["surname", "pseudo", "name", "email"],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||||
'OPTIONS': {
|
"OPTIONS": {
|
||||||
'min_length': 8,
|
"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",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
@ -73,9 +73,8 @@ an instance of a model (either Model.can_xxx or instance.can_xxx)
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
from django.template.base import Node, NodeList
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.template.base import Node, NodeList
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
@ -202,7 +201,7 @@ def acl_fct(callback, reverse):
|
||||||
@register.tag("cannot_edit_history")
|
@register.tag("cannot_edit_history")
|
||||||
def acl_history_filter(parser, token):
|
def acl_history_filter(parser, token):
|
||||||
"""Templatetag for acl checking on history."""
|
"""Templatetag for acl checking on history."""
|
||||||
tag_name, = token.split_contents()
|
(tag_name,) = token.split_contents()
|
||||||
|
|
||||||
callback = get_callback(tag_name)
|
callback = get_callback(tag_name)
|
||||||
oknodes = parser.parse(("acl_else", "acl_end"))
|
oknodes = parser.parse(("acl_else", "acl_end"))
|
||||||
|
|
|
@ -20,10 +20,12 @@
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
|
|
||||||
from .url_insert_param import url_insert_param
|
from .url_insert_param import url_insert_param
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def pagination_insert_page_and_id(url, page=1, id=None, **kwargs):
|
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"
|
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"]
|
page_arg = kwargs["page_arg"]
|
||||||
|
|
||||||
args = { "url": url, page_arg: page}
|
args = {"url": url, page_arg: page}
|
||||||
new_url = url_insert_param(**args)
|
new_url = url_insert_param(**args)
|
||||||
|
|
||||||
if id != None:
|
if id != None:
|
||||||
|
|
|
@ -26,8 +26,8 @@ which indicated if a user can creates an account by himself
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
from preferences.models import OptionalUser
|
|
||||||
|
|
||||||
|
from preferences.models import OptionalUser
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
|
@ -24,14 +24,13 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.urls import include, path
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django.urls import include, path
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.views.generic import RedirectView
|
from django.views.generic import RedirectView
|
||||||
|
|
||||||
from .settings_local import OPTIONNAL_APPS_RE2O
|
from .settings_local import OPTIONNAL_APPS_RE2O
|
||||||
|
from .views import about_page, contact_page, handler404, handler500, index
|
||||||
from .views import index, about_page, contact_page, handler404, handler500
|
|
||||||
|
|
||||||
# Admin site configuration
|
# Admin site configuration
|
||||||
admin.site.index_title = _("Homepage")
|
admin.site.index_title = _("Homepage")
|
||||||
|
|
|
@ -36,14 +36,14 @@ Functions:
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
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.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 cotisations.models import Cotisation, Facture, Vente
|
||||||
from machines.models import Interface, Machine
|
from machines.models import Interface, Machine
|
||||||
from users.models import Adherent, User, Ban, Whitelist
|
|
||||||
from preferences.models import AssoOption
|
from preferences.models import AssoOption
|
||||||
|
from users.models import Adherent, Ban, User, Whitelist
|
||||||
|
|
||||||
|
|
||||||
def get_group_having_permission(*permission_name):
|
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
|
- on user_type (adherent or club) is specified
|
||||||
Returns the filter"""
|
Returns the filter"""
|
||||||
if dormitory:
|
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":
|
if user_type == "adherent":
|
||||||
query_filter &= Q(adherent__isnull=False)
|
query_filter &= Q(adherent__isnull=False)
|
||||||
if user_type == "club":
|
if user_type == "club":
|
||||||
|
@ -91,18 +93,20 @@ def filter_results(query_filter, dormitory, user_type):
|
||||||
return query_filter
|
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
|
"""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
|
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.
|
membership is registered at the desired search_time.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
search_time (django datetime): Datetime to perform this search,
|
search_time (django datetime): Datetime to perform this search,
|
||||||
if not provided, search_time will be set à timezone.now()
|
if not provided, search_time will be set à timezone.now()
|
||||||
including_asso (boolean): Decide if org itself is included in results
|
including_asso (boolean): Decide if org itself is included in results
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
django queryset: Django queryset containing all users with valid membership
|
django queryset: Django queryset containing all users with valid membership
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if search_time is None:
|
if search_time is None:
|
||||||
|
@ -110,9 +114,9 @@ def all_adherent(search_time=None, including_asso=True, dormitory=None, user_typ
|
||||||
filter_user = Q(
|
filter_user = Q(
|
||||||
facture__in=Facture.objects.filter(
|
facture__in=Facture.objects.filter(
|
||||||
vente__cotisation__in=Cotisation.objects.filter(
|
vente__cotisation__in=Cotisation.objects.filter(
|
||||||
Q(vente__facture__facture__valid=True) &
|
Q(vente__facture__facture__valid=True)
|
||||||
Q(date_start_memb__lt=search_time) &
|
& Q(date_start_memb__lt=search_time)
|
||||||
Q(date_end_memb__gt=search_time)
|
& Q(date_end_memb__gt=search_time)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -126,15 +130,15 @@ def all_adherent(search_time=None, including_asso=True, dormitory=None, user_typ
|
||||||
|
|
||||||
def all_baned(search_time=None, dormitory=None, user_type="all"):
|
def all_baned(search_time=None, dormitory=None, user_type="all"):
|
||||||
"""Return all people who are banned at org. Optimised to make only one
|
"""Return all people who are banned at org. Optimised to make only one
|
||||||
sql query. Build a filter and then apply it to User. Check for each user
|
sql query. Build a filter and then apply it to User. Check for each user
|
||||||
banned at the desired search_time.
|
banned at the desired search_time.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
search_time (django datetime): Datetime to perform this search,
|
search_time (django datetime): Datetime to perform this search,
|
||||||
if not provided, search_time will be set à timezone.now()
|
if not provided, search_time will be set à timezone.now()
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
django queryset: Django queryset containing all users banned
|
django queryset: Django queryset containing all users banned
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if search_time is None:
|
if search_time is None:
|
||||||
|
@ -152,13 +156,13 @@ def all_whitelisted(search_time=None, dormitory=None, user_type="all"):
|
||||||
"""Return all people who have a free access at org. Optimised to make only one
|
"""Return all people who have a free access at org. Optimised to make only one
|
||||||
sql query. Build a filter and then apply it to User. Check for each user with a
|
sql query. Build a filter and then apply it to User. Check for each user with a
|
||||||
whitelisted free access at the desired search_time.
|
whitelisted free access at the desired search_time.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
search_time (django datetime): Datetime to perform this search,
|
search_time (django datetime): Datetime to perform this search,
|
||||||
if not provided, search_time will be set à timezone.now()
|
if not provided, search_time will be set à timezone.now()
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
django queryset: Django queryset containing all users whitelisted
|
django queryset: Django queryset containing all users whitelisted
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if search_time is None:
|
if search_time is None:
|
||||||
|
@ -191,9 +195,9 @@ def all_conn(search_time=None, including_asso=True, dormitory=None, user_type="a
|
||||||
filter_user = Q(
|
filter_user = Q(
|
||||||
facture__in=Facture.objects.filter(
|
facture__in=Facture.objects.filter(
|
||||||
vente__cotisation__in=Cotisation.objects.filter(
|
vente__cotisation__in=Cotisation.objects.filter(
|
||||||
Q(vente__facture__facture__valid=True) &
|
Q(vente__facture__facture__valid=True)
|
||||||
Q(date_start_con__lt=search_time) &
|
& Q(date_start_con__lt=search_time)
|
||||||
Q(date_end_con__gt=search_time)
|
& Q(date_end_con__gt=search_time)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -205,9 +209,11 @@ def all_conn(search_time=None, including_asso=True, dormitory=None, user_type="a
|
||||||
return User.objects.filter(filter_user).distinct()
|
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.
|
"""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
|
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.
|
with a whitelist, or a valid paid access, except banned users.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
@ -216,14 +222,13 @@ def all_has_access(search_time=None, including_asso=True, dormitory=None, user_t
|
||||||
including_asso (boolean): Decide if org itself is included in results
|
including_asso (boolean): Decide if org itself is included in results
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
django queryset: Django queryset containing all valid connection users
|
django queryset: Django queryset containing all valid connection users
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if search_time is None:
|
if search_time is None:
|
||||||
search_time = timezone.now()
|
search_time = timezone.now()
|
||||||
filter_user = (
|
filter_user = Q(state=User.STATE_ACTIVE) & ~Q(
|
||||||
Q(state=User.STATE_ACTIVE)
|
email_state=User.EMAIL_STATE_UNVERIFIED
|
||||||
& ~Q(email_state=User.EMAIL_STATE_UNVERIFIED)
|
|
||||||
)
|
)
|
||||||
if including_asso:
|
if including_asso:
|
||||||
asso_user = AssoOption.get_cached_value("utilisateur_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 |= Q(id=asso_user.id)
|
||||||
filter_user = filter_results(filter_user, dormitory, user_type)
|
filter_user = filter_results(filter_user, dormitory, user_type)
|
||||||
return User.objects.filter(
|
return User.objects.filter(
|
||||||
Q(filter_user) & (
|
Q(filter_user)
|
||||||
|
& (
|
||||||
Q(
|
Q(
|
||||||
id__in=all_whitelisted(search_time=search_time, dormitory=dormitory, user_type=user_type)
|
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)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
) & ~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()
|
).distinct()
|
||||||
|
|
||||||
|
@ -257,7 +280,7 @@ def filter_active_interfaces(interface_set):
|
||||||
interface_set (django queryset): A queryset of interfaces to perform filter
|
interface_set (django queryset): A queryset of interfaces to perform filter
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
django filter: Django filter to apply to an interfaces queryset,
|
django filter: Django filter to apply to an interfaces queryset,
|
||||||
will return when applied all active interfaces, related with
|
will return when applied all active interfaces, related with
|
||||||
a user with valid membership
|
a user with valid membership
|
||||||
|
|
||||||
|
@ -289,7 +312,7 @@ def filter_complete_interfaces(interface_set):
|
||||||
interface_set (django queryset): A queryset of interfaces to perform filter
|
interface_set (django queryset): A queryset of interfaces to perform filter
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
django filter: Django filter to apply to an interfaces queryset,
|
django filter: Django filter to apply to an interfaces queryset,
|
||||||
will return when applied all active interfaces, related with
|
will return when applied all active interfaces, related with
|
||||||
a user with valid membership
|
a user with valid membership
|
||||||
|
|
||||||
|
|
|
@ -26,34 +26,28 @@ Welcom main page view, and several template widely used in re2o views
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
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.shortcuts import render
|
||||||
from django.template.context_processors import csrf
|
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 django.utils.decorators import method_decorator
|
||||||
from dal import autocomplete
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from preferences.models import (
|
from preferences.models import (AssoOption, GeneralOption, HomeOption,
|
||||||
Service,
|
MailContact, Mandate, Service)
|
||||||
MailContact,
|
from re2o.settings_local import OPTIONNAL_APPS_RE2O
|
||||||
AssoOption,
|
|
||||||
HomeOption,
|
|
||||||
GeneralOption,
|
|
||||||
Mandate,
|
|
||||||
)
|
|
||||||
|
|
||||||
from .contributors import CONTRIBUTORS
|
from .contributors import CONTRIBUTORS
|
||||||
from importlib import import_module
|
|
||||||
from re2o.settings_local import OPTIONNAL_APPS_RE2O
|
|
||||||
|
|
||||||
|
|
||||||
def form(ctx, template, request):
|
def form(ctx, template, request):
|
||||||
"""Global template function, used in all re2o views, for building a render with context,
|
"""Global template function, used in all re2o views, for building a render with context,
|
||||||
template and request. Adding csrf.
|
template and request. Adding csrf.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
ctx (dict): Dict of values to transfer to template
|
ctx (dict): Dict of values to transfer to template
|
||||||
template (django template): The django template of this view
|
template (django template): The django template of this view
|
||||||
|
@ -70,8 +64,8 @@ def form(ctx, template, request):
|
||||||
def index(request):
|
def index(request):
|
||||||
"""Display all services provided on main page
|
"""Display all services provided on main page
|
||||||
|
|
||||||
Returns: a form with all services linked and description, and social media
|
Returns: a form with all services linked and description, and social media
|
||||||
link if provided.
|
link if provided.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
services = [[], [], []]
|
services = [[], [], []]
|
||||||
|
@ -95,9 +89,9 @@ def index(request):
|
||||||
|
|
||||||
|
|
||||||
def about_page(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
|
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()
|
option = AssoOption.objects.get()
|
||||||
general = GeneralOption.objects.get()
|
general = GeneralOption.objects.get()
|
||||||
git_info_contributors = CONTRIBUTORS
|
git_info_contributors = CONTRIBUTORS
|
||||||
|
@ -197,4 +191,3 @@ class AutocompleteLoggedOutViewMixin(autocomplete.Select2QuerySetView):
|
||||||
|
|
||||||
class AutocompleteViewMixin(LoginRequiredMixin, AutocompleteLoggedOutViewMixin):
|
class AutocompleteViewMixin(LoginRequiredMixin, AutocompleteLoggedOutViewMixin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
# quelques clics.
|
# quelques clics.
|
||||||
#
|
#
|
||||||
# Copyright © 2021 Gabriel Détraz
|
# Copyright © 2021 Gabriel Détraz
|
||||||
# Copyright © 2021 Jean-Romain Garnier
|
# Copyright © 2021 Jean-Romain Garnier
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
@ -25,12 +25,12 @@ Re2o Forms and ModelForms Widgets.
|
||||||
Used in others forms for using autocomplete engine.
|
Used in others forms for using autocomplete engine.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
from dal import autocomplete
|
from dal import autocomplete
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
|
||||||
class AutocompleteModelWidget(autocomplete.ModelSelect2):
|
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
|
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):
|
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
|
See https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#passing-options-to-select2
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
@ -32,12 +32,11 @@ https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from os.path import dirname
|
|
||||||
import sys
|
import sys
|
||||||
|
from os.path import dirname
|
||||||
|
|
||||||
from django.core.wsgi import get_wsgi_application
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
|
|
||||||
sys.path.append(dirname(dirname(__file__)))
|
sys.path.append(dirname(dirname(__file__)))
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "re2o.settings")
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "re2o.settings")
|
||||||
|
|
||||||
|
|
|
@ -8,4 +8,4 @@ from django.apps import AppConfig
|
||||||
class SearchConfig(AppConfig):
|
class SearchConfig(AppConfig):
|
||||||
"""Configuration of search app."""
|
"""Configuration of search app."""
|
||||||
|
|
||||||
name = "search"
|
name = "search"
|
||||||
|
|
|
@ -28,19 +28,16 @@ Gplv2"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
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 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 cotisations.models import Facture
|
||||||
|
from machines.models import Machine
|
||||||
from preferences.models import GeneralOption
|
from preferences.models import GeneralOption
|
||||||
from re2o.base import SortTable, re2o_paginator
|
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
|
# List of fields the search applies to
|
||||||
FILTER_FIELDS = [
|
FILTER_FIELDS = [
|
||||||
|
|
|
@ -27,6 +27,7 @@ from __future__ import unicode_literals
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.forms import Form
|
from django.forms import Form
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from re2o.base import get_input_formats_help_text
|
from re2o.base import get_input_formats_help_text
|
||||||
|
|
||||||
CHOICES_USER = (
|
CHOICES_USER = (
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue