models.py 12.2 KB
Newer Older
1
# -*- mode: python; coding: utf-8 -*-
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# Re2o un logiciel d'administration développé initiallement au rezometz. Il
# se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics.
#
# Copyright © 2017  Gabriel Détraz
# Copyright © 2017  Goulven Kermarec
# Copyright © 2017  Augustin Lemesle
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 24 25
"""
Reglages généraux, machines, utilisateurs, mail, general pour l'application.
"""
26
from __future__ import unicode_literals
27

28
from django.utils.functional import cached_property
29
from django.db import models
30
from django.db.models.signals import post_save
31
from django.dispatch import receiver
32
from django.core.cache import cache
33
from django.forms import ValidationError
34
from django.utils.translation import ugettext_lazy as _
35

36
import machines.models
chirac's avatar
chirac committed
37
from re2o.mixins import AclMixin
38

Maël Kervella's avatar
Maël Kervella committed
39

Gabriel Detraz's avatar
Gabriel Detraz committed
40
class PreferencesModel(models.Model):
41 42 43
    """ Base object for the Preferences objects
    Defines methods to handle the cache of the settings (they should
    not change a lot) """
Gabriel Detraz's avatar
Gabriel Detraz committed
44 45
    @classmethod
    def set_in_cache(cls):
46
        """ Save the preferences in a server-side cache """
Gabriel Detraz's avatar
Gabriel Detraz committed
47 48 49 50 51 52
        instance, _created = cls.objects.get_or_create()
        cache.set(cls().__class__.__name__.lower(), instance, None)
        return instance

    @classmethod
    def get_cached_value(cls, key):
53
        """ Get the preferences from the server-side cache """
Gabriel Detraz's avatar
Gabriel Detraz committed
54
        instance = cache.get(cls().__class__.__name__.lower())
Maël Kervella's avatar
Maël Kervella committed
55
        if instance is None:
56
            instance = cls.set_in_cache()
Gabriel Detraz's avatar
Gabriel Detraz committed
57 58 59 60 61 62
        return getattr(instance, key)

    class Meta:
        abstract = True


chirac's avatar
chirac committed
63
class OptionalUser(AclMixin, PreferencesModel):
64 65
    """Options pour l'user : obligation ou nom du telephone,
    activation ou non du solde, autorisation du negatif, fingerprint etc"""
66

67 68
    is_tel_mandatory = models.BooleanField(default=True)
    gpg_fingerprint = models.BooleanField(default=True)
69
    all_can_create_club = models.BooleanField(
70
        default=False,
71
        help_text=_("Users can create a club")
72 73 74
    )
    all_can_create_adherent = models.BooleanField(
        default=False,
75
        help_text=_("Users can create a member"),
76
    )
77 78
    self_adhesion = models.BooleanField(
        default=False,
79
        help_text=_("A new user can create their account on Re2o")
80
    )
81 82 83 84 85 86
    shell_default = models.OneToOneField(
        'users.ListShell',
        on_delete=models.PROTECT,
        blank=True,
        null=True
    )
87 88
    self_change_shell = models.BooleanField(
        default=False,
89
        help_text=_("Users can edit their shell")
90
    )
91
    local_email_accounts_enabled = models.BooleanField(
92
        default=False,
93
        help_text=_("Enable local email accounts for users")
94
    )
95
    local_email_domain = models.CharField(
96 97 98
        max_length=32,
        default="@example.org",
        help_text=_("Domain to use for local email accounts")
99
    )
100
    max_email_address = models.IntegerField(
101 102 103
        default=15,
        help_text=_("Maximum number of local email addresses for a standard"
                    " user")
104
    )
105

106 107
    class Meta:
        permissions = (
108
            ("view_optionaluser", _("Can view the user options")),
109
        )
110
        verbose_name = _("user options")
111

112
    def clean(self):
Gabriel Detraz's avatar
Gabriel Detraz committed
113 114
        """Clean model:
        Check the mail_extension
115
        """
116
        if self.local_email_domain[0] != "@":
117
            raise ValidationError(_("Email domain must begin with @"))
118

119

120
@receiver(post_save, sender=OptionalUser)
121
def optionaluser_post_save(**kwargs):
122
    """Ecriture dans le cache"""
123 124 125 126
    user_pref = kwargs['instance']
    user_pref.set_in_cache()


chirac's avatar
chirac committed
127
class OptionalMachine(AclMixin, PreferencesModel):
128 129
    """Options pour les machines : maximum de machines ou d'alias par user
    sans droit, activation de l'ipv6"""
130

131 132 133 134
    SLAAC = 'SLAAC'
    DHCPV6 = 'DHCPV6'
    DISABLED = 'DISABLED'
    CHOICE_IPV6 = (
135 136 137
        (SLAAC, _("Autoconfiguration by RA")),
        (DHCPV6, _("IP addresses assigning by DHCPv6")),
        (DISABLED, _("Disabled")),
138 139
    )

140 141 142
    password_machine = models.BooleanField(default=False)
    max_lambdauser_interfaces = models.IntegerField(default=10)
    max_lambdauser_aliases = models.IntegerField(default=10)
143 144 145 146 147
    ipv6_mode = models.CharField(
        max_length=32,
        choices=CHOICE_IPV6,
        default='DISABLED'
    )
148
    create_machine = models.BooleanField(
149
        default=True
150
    )
151 152 153

    @cached_property
    def ipv6(self):
154
        """ Check if the IPv6 option is activated """
Maël Kervella's avatar
Maël Kervella committed
155
        return not self.get_cached_value('ipv6_mode') == 'DISABLED'
156

157 158
    class Meta:
        permissions = (
159
            ("view_optionalmachine", _("Can view the machine options")),
160
        )
161
        verbose_name = _("machine options")
162

163

164
@receiver(post_save, sender=OptionalMachine)
165
def optionalmachine_post_save(**kwargs):
166
    """Synchronisation ipv6 et ecriture dans le cache"""
167
    machine_pref = kwargs['instance']
168
    machine_pref.set_in_cache()
169 170 171 172 173
    if machine_pref.ipv6_mode != "DISABLED":
        for interface in machines.models.Interface.objects.all():
            interface.sync_ipv6()


chirac's avatar
chirac committed
174
class OptionalTopologie(AclMixin, PreferencesModel):
175 176
    """Reglages pour la topologie : mode d'accès radius, vlan où placer
    les machines en accept ou reject"""
177 178 179
    MACHINE = 'MACHINE'
    DEFINED = 'DEFINED'
    CHOICE_RADIUS = (
180 181
        (MACHINE, _("On the IP range's VLAN of the machine")),
        (DEFINED, _("Preset in 'VLAN for machines accepted by RADIUS'")),
182
    )
183

184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
    radius_general_policy = models.CharField(
        max_length=32,
        choices=CHOICE_RADIUS,
        default='DEFINED'
    )
    vlan_decision_ok = models.OneToOneField(
        'machines.Vlan',
        on_delete=models.PROTECT,
        related_name='decision_ok',
        blank=True,
        null=True
    )
    vlan_decision_nok = models.OneToOneField(
        'machines.Vlan',
        on_delete=models.PROTECT,
        related_name='decision_nok',
        blank=True,
        null=True
    )
203 204 205 206 207 208 209
    vlan_non_member = models.OneToOneField(
        'machines.Vlan',
        on_delete=models.PROTECT,
        related_name='decision_non_member',
        blank=True,
        null=True
    )
210

211 212
    class Meta:
        permissions = (
213
            ("view_optionaltopologie", _("Can view the topology options")),
214
        )
215
        verbose_name = _("topology options")
216

217

218
@receiver(post_save, sender=OptionalTopologie)
219
def optionaltopologie_post_save(**kwargs):
220
    """Ecriture dans le cache"""
221 222 223 224
    topologie_pref = kwargs['instance']
    topologie_pref.set_in_cache()


chirac's avatar
chirac committed
225
class GeneralOption(AclMixin, PreferencesModel):
226 227
    """Options générales : nombre de resultats par page, nom du site,
    temps où les liens sont valides"""
228

229
    general_message_fr = models.TextField(
230 231
        default="",
        blank=True,
232 233
        help_text=_("General message displayed on the French version of the"
                    " website (e.g. in case of maintenance)")
234 235 236 237
    )
    general_message_en = models.TextField(
        default="",
        blank=True,
238 239
        help_text=_("General message displayed on the English version of the"
                    " website (e.g. in case of maintenance)")
240
    )
241 242 243
    search_display_page = models.IntegerField(default=15)
    pagination_number = models.IntegerField(default=25)
    pagination_large_number = models.IntegerField(default=8)
244 245
    req_expire_hrs = models.IntegerField(default=48)
    site_name = models.CharField(max_length=32, default="Re2o")
246
    email_from = models.EmailField(default="www-data@example.com")
247 248 249 250 251
    GTU_sum_up = models.TextField(
        default="",
        blank=True,
    )
    GTU = models.FileField(
Maël Kervella's avatar
Maël Kervella committed
252
        upload_to='',
253 254 255 256
        default="",
        null=True,
        blank=True,
    )
257

258 259
    class Meta:
        permissions = (
260
            ("view_generaloption", _("Can view the general options")),
261
        )
262
        verbose_name = _("general options")
263

264

265
@receiver(post_save, sender=GeneralOption)
266
def generaloption_post_save(**kwargs):
267
    """Ecriture dans le cache"""
268 269 270 271
    general_pref = kwargs['instance']
    general_pref.set_in_cache()


chirac's avatar
chirac committed
272
class Service(AclMixin, models.Model):
273 274
    """Liste des services affichés sur la page d'accueil : url, description,
    image et nom"""
275 276 277
    name = models.CharField(max_length=32)
    url = models.URLField()
    description = models.TextField()
278
    image = models.ImageField(upload_to='logo', blank=True)
279

280 281
    class Meta:
        permissions = (
282
            ("view_service", _("Can view the service options")),
283
        )
284 285
        verbose_name = _("service")
        verbose_name_plural =_("services")
286

287 288 289
    def __str__(self):
        return str(self.name)

290
class MailContact(AclMixin, models.Model):
291
    """Contact email adress with a commentary."""
292 293 294

    address = models.EmailField(
        default = "contact@example.org",
295
        help_text = _("Contact email address")
296 297 298 299 300
    )

    commentary = models.CharField(
        blank = True,
        null = True,
301
        help_text = _(
302
            "Description of the associated email address."),
303 304 305
        max_length = 256
    )

306 307 308 309
    @cached_property
    def get_name(self):
        return self.address.split("@")[0]

310 311
    class Meta:
        permissions = (
312
            ("view_mailcontact", _("Can view a contact email address object")),
313
        )
314 315
        verbose_name = _("contact email address")
        verbose_name_plural = _("contact email addresses")
316 317 318 319

    def __str__(self):
        return(self.address)

320

chirac's avatar
chirac committed
321
class AssoOption(AclMixin, PreferencesModel):
322
    """Options générales de l'asso : siret, addresse, nom, etc"""
323

324
    name = models.CharField(
325
        default=_("Networking organisation school Something"),
326 327
        max_length=256
    )
328
    siret = models.CharField(default="00000000000000", max_length=32)
329 330
    adresse1 = models.CharField(default=_("Threadneedle Street"), max_length=128)
    adresse2 = models.CharField(default=_("London EC2R 8AH"), max_length=128)
331 332
    contact = models.EmailField(default="contact@example.org")
    telephone = models.CharField(max_length=15, default="0000000000")
333
    pseudo = models.CharField(default=_("Organisation"), max_length=32)
334 335 336 337 338 339
    utilisateur_asso = models.OneToOneField(
        'users.User',
        on_delete=models.PROTECT,
        blank=True,
        null=True
    )
340 341 342 343
    description = models.TextField(
        null=True,
        blank=True,
    )
344

345 346
    class Meta:
        permissions = (
347
            ("view_assooption", _("Can view the organisation options")),
348
        )
349
        verbose_name = _("organisation options")
350

351

352
@receiver(post_save, sender=AssoOption)
353
def assooption_post_save(**kwargs):
354
    """Ecriture dans le cache"""
355 356 357 358
    asso_pref = kwargs['instance']
    asso_pref.set_in_cache()


Gabriel Detraz's avatar
Gabriel Detraz committed
359 360
class HomeOption(AclMixin, PreferencesModel):
    """Settings of the home page (facebook/twitter etc)"""
Gabriel Detraz's avatar
Gabriel Detraz committed
361 362 363

    facebook_url = models.URLField(
        null=True,
364
        blank=True
Gabriel Detraz's avatar
Gabriel Detraz committed
365 366 367
    )
    twitter_url = models.URLField(
        null=True,
368
        blank=True
Gabriel Detraz's avatar
Gabriel Detraz committed
369 370 371 372
    )
    twitter_account_name = models.CharField(
        max_length=32,
        null=True,
373
        blank=True
Gabriel Detraz's avatar
Gabriel Detraz committed
374 375 376 377
    )

    class Meta:
        permissions = (
378
            ("view_homeoption", _("Can view the homepage options")),
Gabriel Detraz's avatar
Gabriel Detraz committed
379
        )
380
        verbose_name = _("homepage options")
Gabriel Detraz's avatar
Gabriel Detraz committed
381 382


Gabriel Detraz's avatar
Gabriel Detraz committed
383 384
@receiver(post_save, sender=HomeOption)
def homeoption_post_save(**kwargs):
Gabriel Detraz's avatar
Gabriel Detraz committed
385
    """Ecriture dans le cache"""
Gabriel Detraz's avatar
Gabriel Detraz committed
386 387
    home_pref = kwargs['instance']
    home_pref.set_in_cache()
Gabriel Detraz's avatar
Gabriel Detraz committed
388 389


chirac's avatar
chirac committed
390
class MailMessageOption(AclMixin, models.Model):
391
    """Reglages, mail de bienvenue et autre"""
392 393 394

    welcome_mail_fr = models.TextField(default="")
    welcome_mail_en = models.TextField(default="")
395

396 397
    class Meta:
        permissions = (
398 399
            ("view_mailmessageoption", _("Can view the email message"
                                         " options")),
400
        )
401 402
        verbose_name = _("email message options")