mirror of https://gitlab.federez.net/re2o/re2o
73 changed files with 2153 additions and 187 deletions
@ -0,0 +1,20 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.10.7 on 2018-06-26 19:31 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('preferences', '0034_auto_20180416_1120'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AddField( |
|||
model_name='optionaluser', |
|||
name='mail_extension', |
|||
field=models.CharField(default='@example.org', help_text='Extension principale pour les mails internes', max_length=32), |
|||
), |
|||
] |
|||
@ -0,0 +1,20 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.10.7 on 2018-06-29 16:01 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('preferences', '0035_optionaluser_mail_extension'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AddField( |
|||
model_name='optionaluser', |
|||
name='mail_accounts', |
|||
field=models.BooleanField(default=False, help_text='Activation des comptes mails pour les utilisateurs'), |
|||
), |
|||
] |
|||
@ -0,0 +1,20 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.10.7 on 2018-06-30 12:32 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('preferences', '0036_optionaluser_mail_accounts'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AddField( |
|||
model_name='optionaluser', |
|||
name='max_mail_alias', |
|||
field=models.IntegerField(default=15, help_text="Nombre maximal d'alias pour un utilisateur lambda"), |
|||
), |
|||
] |
|||
@ -0,0 +1,28 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.10.7 on 2018-06-30 15:27 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
import re2o.mixins |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('preferences', '0037_optionaluser_max_mail_alias'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.CreateModel( |
|||
name='MailContact', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('address', models.EmailField(default='contact@example.org', help_text='Adresse mail de contact', max_length=254)), |
|||
('commentary', models.CharField(blank=True, help_text="Description de l'utilisation de l'adresse mail associée", max_length=256, null=True)), |
|||
], |
|||
options={ |
|||
'permissions': (('view_mailcontact', 'Peut voir les mails de contact'),), |
|||
}, |
|||
bases=(re2o.mixins.AclMixin, models.Model), |
|||
), |
|||
] |
|||
@ -0,0 +1,45 @@ |
|||
{% comment %} |
|||
Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
se veut agnostique au réseau considéré, de manière à être installable en |
|||
quelques clics. |
|||
|
|||
Copyright © 2017 Gabriel Détraz |
|||
Copyright © 2017 Goulven Kermarec |
|||
Copyright © 2017 Augustin Lemesle |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License along |
|||
with this program; if not, write to the Free Software Foundation, Inc., |
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
{% endcomment %} |
|||
{% load acl %} |
|||
<table class="table table-striped"> |
|||
<thead> |
|||
<tr> |
|||
<th>Adresse</th> |
|||
<th>Commentaire</th> |
|||
<th></th> |
|||
</tr> |
|||
</thead> |
|||
{% for mailcontact in mailcontact_list %} |
|||
<tr> |
|||
<td>{{ mailcontact.address }}</td> |
|||
<td>{{ mailcontact.commentary }}</td> |
|||
<td class="text-right"> |
|||
{% can_edit mailcontact %} |
|||
{% include 'buttons/edit.html' with href='preferences:edit-mailcontact' id=mailcontact.id %} |
|||
{% acl_end %} |
|||
{% include 'buttons/history.html' with href='preferences:history' name='mailcontact' id=mailcontact.id %} |
|||
</td> |
|||
</tr> |
|||
{% endfor %} |
|||
</table> |
|||
@ -0,0 +1,3 @@ |
|||
from django.contrib import admin |
|||
|
|||
# Register your models here. |
|||
@ -0,0 +1,5 @@ |
|||
from django.apps import AppConfig |
|||
|
|||
|
|||
class PrinterConfig(AppConfig): |
|||
name = 'printer' |
|||
@ -0,0 +1,37 @@ |
|||
# -*- mode: python; coding: utf-8 -*- |
|||
|
|||
"""printer.forms |
|||
Form to add, edit, cancel printer jobs. |
|||
Author : Maxime Bombar <bombar@crans.org>. |
|||
Date : 29/06/2018 |
|||
""" |
|||
|
|||
from django import forms |
|||
from django.forms import ( |
|||
Form, |
|||
ModelForm, |
|||
) |
|||
|
|||
import itertools |
|||
|
|||
from re2o.mixins import FormRevMixin |
|||
|
|||
from .models import ( |
|||
JobWithOptions, |
|||
) |
|||
|
|||
|
|||
class JobWithOptionsForm(FormRevMixin, ModelForm): |
|||
def __init__(self, *args, **kwargs): |
|||
prefix = kwargs.pop('prefix', self.Meta.model.__name__) |
|||
super(JobWithOptionsForm, self).__init__(*args, prefix=prefix, **kwargs) |
|||
|
|||
class Meta: |
|||
model = JobWithOptions |
|||
fields = [ |
|||
'file', |
|||
'color', |
|||
'disposition', |
|||
'count', |
|||
] |
|||
|
|||
@ -0,0 +1,26 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.10.7 on 2018-06-28 18:30 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.conf import settings |
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
initial = True |
|||
|
|||
dependencies = [ |
|||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.CreateModel( |
|||
name='Dummy', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), |
|||
], |
|||
), |
|||
] |
|||
@ -0,0 +1,48 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.10.7 on 2018-06-28 18:32 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.conf import settings |
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
import printer.models |
|||
import printer.validators |
|||
import re2o.mixins |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), |
|||
('printer', '0001_initial'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.CreateModel( |
|||
name='JobWithOptions', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('file', models.FileField(upload_to=printer.models.user_printing_path, validators=[printer.validators.FileValidator(allowed_types=['application/pdf'], max_size=10485760)])), |
|||
('starttime', models.DateTimeField(auto_now_add=True)), |
|||
('endtime', models.DateTimeField(null=True)), |
|||
('status', models.CharField(choices=[('Printable', 'Printable'), ('Running', 'Running'), ('Cancelled', 'Cancelled'), ('Finished', 'Finished')], max_length=255)), |
|||
('price', models.IntegerField(default=0)), |
|||
('format', models.CharField(choices=[('A4', 'A4'), ('A3', 'A4')], default='A4', max_length=255)), |
|||
('color', models.CharField(choices=[('Greyscale', 'Greyscale'), ('Color', 'Color')], default='Greyscale', max_length=255)), |
|||
('disposition', models.CharField(choices=[('TwoSided', 'Two sided'), ('OneSided', 'One sided'), ('Booklet', 'Booklet')], default='TwoSided', max_length=255)), |
|||
('count', models.PositiveIntegerField(default=1)), |
|||
('stapling', models.CharField(choices=[('None', 'None'), ('TopLeft', 'One top left'), ('TopRight', 'One top right'), ('LeftSided', 'Two left sided'), ('RightSided', 'Two right sided')], default='None', max_length=255)), |
|||
('perforation', models.CharField(choices=[('None', 'None'), ('TwoLeftSidedHoles', 'Two left sided holes'), ('TwoRightSidedHoles', 'Two right sided holes'), ('TwoTopHoles', 'Two top holes'), ('TwoBottomHoles', 'Two bottom holes'), ('FourLeftSidedHoles', 'Four left sided holes'), ('FourRightSidedHoles', 'Four right sided holes')], default='None', max_length=255)), |
|||
('printAs', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='print_as_user', to=settings.AUTH_USER_MODEL)), |
|||
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), |
|||
], |
|||
bases=(re2o.mixins.RevMixin, models.Model), |
|||
), |
|||
migrations.RemoveField( |
|||
model_name='dummy', |
|||
name='user', |
|||
), |
|||
migrations.DeleteModel( |
|||
name='Dummy', |
|||
), |
|||
] |
|||
@ -0,0 +1,116 @@ |
|||
# -*- mode: python; coding: utf-8 -*- |
|||
|
|||
"""printer.models |
|||
Models of the printer application |
|||
Author : Maxime Bombar <bombar@crans.org>. |
|||
Date : 29/06/2018 |
|||
""" |
|||
|
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import models |
|||
from django.forms import ValidationError |
|||
from django.utils.translation import ugettext_lazy as _ |
|||
from django.template.defaultfilters import filesizeformat |
|||
|
|||
from re2o.mixins import RevMixin |
|||
|
|||
import users.models |
|||
|
|||
from .validators import ( |
|||
FileValidator, |
|||
) |
|||
|
|||
from .settings import ( |
|||
MAX_PRINTFILE_SIZE, |
|||
ALLOWED_TYPES, |
|||
) |
|||
|
|||
|
|||
""" |
|||
- ```user_printing_path``` is a function that returns the path of the uploaded file, used with the FileField. |
|||
- ```Job``` is the main model of a printer job. His parent is the ```user``` model. |
|||
""" |
|||
|
|||
|
|||
def user_printing_path(instance, filename): |
|||
# File will be uploaded to MEDIA_ROOT/printings/user_<id>/<filename> |
|||
return 'printings/user_{0}/{1}'.format(instance.user.id, filename) |
|||
|
|||
|
|||
class JobWithOptions(RevMixin, models.Model): |
|||
""" |
|||
This is the main model of printer application : |
|||
|
|||
- ```user``` is a ForeignKey to the User Application |
|||
- ```file``` is the file to print |
|||
- ```starttime``` is the time when the job was launched |
|||
- ```endtime``` is the time when the job was stopped. |
|||
A job is stopped when it is either finished or cancelled. |
|||
- ```status``` can be running, finished or cancelled. |
|||
- ```club``` is blank in general. If the job was launched as a club then |
|||
it is the id of the club. |
|||
- ```price``` is the total price of this printing. |
|||
|
|||
Printing Options : |
|||
|
|||
- ```format``` is the paper format. Example: A4. |
|||
- ```color``` is the colorization option. Either Color or Greyscale. |
|||
- ```disposition``` is the paper disposition. |
|||
- ```count``` is the number of copies to be printed. |
|||
- ```stapling``` is the stapling options. |
|||
- ```perforations``` is the perforation options. |
|||
|
|||
|
|||
Parent class : User |
|||
""" |
|||
STATUS_AVAILABLE = ( |
|||
('Printable', 'Printable'), |
|||
('Running', 'Running'), |
|||
('Cancelled', 'Cancelled'), |
|||
('Finished', 'Finished') |
|||
) |
|||
user = models.ForeignKey('users.User', on_delete=models.PROTECT) |
|||
file = models.FileField(upload_to=user_printing_path, validators=[FileValidator(allowed_types=ALLOWED_TYPES, max_size=MAX_PRINTFILE_SIZE)]) |
|||
starttime = models.DateTimeField(auto_now_add=True) |
|||
endtime = models.DateTimeField(null=True) |
|||
status = models.CharField(max_length=255, choices=STATUS_AVAILABLE) |
|||
printAs = models.ForeignKey('users.User', on_delete=models.PROTECT, related_name='print_as_user', null=True) |
|||
price = models.IntegerField(default=0) |
|||
|
|||
FORMAT_AVAILABLE = ( |
|||
('A4', 'A4'), |
|||
('A3', 'A4'), |
|||
) |
|||
COLOR_CHOICES = ( |
|||
('Greyscale', 'Greyscale'), |
|||
('Color', 'Color') |
|||
) |
|||
DISPOSITIONS_AVAILABLE = ( |
|||
('TwoSided', 'Two sided'), |
|||
('OneSided', 'One sided'), |
|||
('Booklet', 'Booklet') |
|||
) |
|||
STAPLING_OPTIONS = ( |
|||
('None', 'None'), |
|||
('TopLeft', 'One top left'), |
|||
('TopRight', 'One top right'), |
|||
('LeftSided', 'Two left sided'), |
|||
('RightSided', 'Two right sided') |
|||
) |
|||
PERFORATION_OPTIONS = ( |
|||
('None', 'None'), |
|||
('TwoLeftSidedHoles', 'Two left sided holes'), |
|||
('TwoRightSidedHoles', 'Two right sided holes'), |
|||
('TwoTopHoles', 'Two top holes'), |
|||
('TwoBottomHoles', 'Two bottom holes'), |
|||
('FourLeftSidedHoles', 'Four left sided holes'), |
|||
('FourRightSidedHoles', 'Four right sided holes') |
|||
) |
|||
|
|||
format = models.CharField(max_length=255, choices=FORMAT_AVAILABLE, default='A4') |
|||
color = models.CharField(max_length=255, choices=COLOR_CHOICES, default='Greyscale') |
|||
disposition = models.CharField(max_length=255, choices=DISPOSITIONS_AVAILABLE, default='TwoSided') |
|||
count = models.PositiveIntegerField(default=1) |
|||
stapling = models.CharField(max_length=255, choices=STAPLING_OPTIONS, default='None') |
|||
perforation = models.CharField(max_length=255, choices=PERFORATION_OPTIONS, default='None') |
|||
@ -0,0 +1,13 @@ |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
"""printer.settings |
|||
Define variables, to be changed into a configuration table. |
|||
""" |
|||
|
|||
|
|||
|
|||
MAX_PRINTFILE_SIZE = 25 * 1024 * 1024 # 25 MB |
|||
ALLOWED_TYPES = ['application/pdf'] |
|||
@ -0,0 +1,12 @@ |
|||
{% extends "base.html" %} |
|||
{% load staticfiles %} |
|||
{% load i18n %} |
|||
|
|||
{% load bootstrap3 %} |
|||
{% load massive_bootstrap_form %} |
|||
{% load static %} |
|||
{% block title %}Printing interface{% endblock %} |
|||
|
|||
{% block content %} |
|||
<h3>{% trans "Failure" %}</h3> |
|||
{% endblock %} |
|||
@ -0,0 +1,87 @@ |
|||
{% extends "base.html" %} |
|||
{% load staticfiles %} |
|||
{% load i18n %} |
|||
|
|||
{% load bootstrap3 %} |
|||
{% load massive_bootstrap_form %} |
|||
{% load static %} |
|||
{% block title %}Printing interface{% endblock %} |
|||
|
|||
{% block content %} |
|||
<form class="form" method="post" enctype="multipart/form-data"> |
|||
{% csrf_token %} |
|||
<h3>{% trans "Printing Menu" %}</h3> |
|||
{{ jobform.management_form }} |
|||
{% bootstrap_formset_errors jobform %} |
|||
<div id="form_set" class="form-group"> |
|||
{% for job in jobform.forms %} |
|||
<div class='file_to_print form-inline'> |
|||
{% bootstrap_form job label_class='sr-only' %} |
|||
<button class="btn btn-danger btn-sm" id="id_form-0-job-remove" type="button"> |
|||
<span class="fa fa-times"></span> |
|||
</button> |
|||
</div> |
|||
{% endfor %} |
|||
</div> |
|||
<input class="btn btn-primary btn-sm" role="button" value="{% trans "Add a file"%}" id="add_one"> |
|||
{% bootstrap_button action_name button_type="submit" icon="star" %} |
|||
</form> |
|||
<script type="text/javascript"> |
|||
|
|||
var template = `{% bootstrap_form jobform.empty_form label_class='sr-only' %} |
|||
<button class="btn btn-danger btn-sm" |
|||
id="id_form-__prefix__-job-remove" type="button"> |
|||
<span class="fa fa-times"></span> |
|||
</button>` |
|||
|
|||
function add_job() { |
|||
var new_index = |
|||
document.getElementsByClassName('file_to_print').length; |
|||
document.getElementById('id_form-TOTAL_FORMS').value ++; |
|||
var new_job = document.createElement('div'); |
|||
new_job.className = 'file_to_print form-inline'; |
|||
new_job.innerHTML = template.replace(/__prefix__/g, new_index); |
|||
document.getElementById('form_set').appendChild(new_job); |
|||
add_listener_for_id(new_index); |
|||
} |
|||
|
|||
|
|||
function del_job(event){ |
|||
var job = event.target.parentNode; |
|||
job.parentNode.removeChild(job); |
|||
document.getElementById('id_form-TOTAL_FORMS').value --; |
|||
} |
|||
|
|||
|
|||
function add_listener_for_id(i){ |
|||
document.getElementById('id_form-' + i.toString() + '-job-remove') |
|||
.addEventListener("click", function(event){ |
|||
var job = event.target.parentNode; |
|||
job.parentNode.removeChild(job); |
|||
document.getElementById('id_form-TOTAL_FORMS').value --; |
|||
} |
|||
) |
|||
} |
|||
|
|||
|
|||
// Add events manager when DOM is fully loaded |
|||
document.addEventListener( |
|||
"DOMContentLoaded", |
|||
function() { |
|||
document.getElementById("add_one") |
|||
.addEventListener("click", add_job, true); |
|||
document.getElementById('id_form-0-job-remove') |
|||
.addEventListener("click", function(event){ |
|||
var job = event.target.parentNode; |
|||
job.parentNode.removeChild(job); |
|||
document.getElementById('id_form-TOTAL_FORMS').value --; |
|||
} |
|||
) |
|||
|
|||
} |
|||
|
|||
); |
|||
|
|||
</script> |
|||
{% endblock %} |
|||
|
|||
@ -0,0 +1,12 @@ |
|||
{% extends "base.html" %} |
|||
{% load staticfiles %} |
|||
{% load i18n %} |
|||
|
|||
{% load bootstrap3 %} |
|||
{% load massive_bootstrap_form %} |
|||
{% load static %} |
|||
{% block title %}Printing interface{% endblock %} |
|||
|
|||
{% block content %} |
|||
<h3>{% trans "Success" %}</h3> |
|||
{% endblock %} |
|||
@ -0,0 +1,3 @@ |
|||
from django.test import TestCase |
|||
|
|||
# Create your tests here. |
|||
@ -0,0 +1,17 @@ |
|||
# -*- coding: utf-8 -*- |
|||
"""printer.urls |
|||
The defined URLs for the printer app |
|||
Author : Maxime Bombar <bombar@crans.org>. |
|||
Date : 29/06/2018 |
|||
""" |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.conf.urls import url |
|||
|
|||
import re2o |
|||
from . import views |
|||
|
|||
urlpatterns = [ |
|||
url(r'^new_job/$', views.new_job, name="new-job"), |
|||
url(r'^success/$', views.success, name="success"), |
|||
] |
|||
@ -0,0 +1,72 @@ |
|||
# -*- mode: python; coding: utf-8 -*- |
|||
|
|||
|
|||
"""printer.validators |
|||
Custom validators useful for printer application. |
|||
Author : Maxime Bombar <bombar@crans.org>. |
|||
Date : 29/06/2018 |
|||
""" |
|||
|
|||
|
|||
|
|||
from django.utils.translation import ugettext_lazy as _ |
|||
from django.core.exceptions import ValidationError |
|||
from django.template.defaultfilters import filesizeformat |
|||
from django.utils.deconstruct import deconstructible |
|||
|
|||
import mimetypes |
|||
|
|||
@deconstructible |
|||
class FileValidator(object): |
|||
""" |
|||
Custom validator for files. It checks the size and mimetype. |
|||
|
|||
Parameters: |
|||
* ```allowed_types``` is an iterable of allowed mimetypes. Example: ['application/pdf'] for a pdf file. |
|||
* ```max_size``` is the maximum size allowed in bytes. Example: 25*1024*1024 for 25 MB. |
|||
|
|||
Usage example: |
|||
|
|||
class UploadModel(models.Model): |
|||
file = fileField(..., validators=FileValidator(allowed_types = ['application/pdf'], max_size=25*1024*1024)) |
|||
""" |
|||
|
|||
|
|||
def __init__(self, *args, **kwargs): |
|||
""" |
|||
Initialize the custom validator. |
|||
By default, all types and size are allowed. |
|||
""" |
|||
self.allowed_types = kwargs.pop('allowed_types', None) |
|||
self.max_size = kwargs.pop('max_size', None) |
|||
|
|||
def __call__(self, value): |
|||
""" |
|||
Check the type and size. |
|||
""" |
|||
|
|||
|
|||
type_message = _("MIME type '%(type)s' is not valid. Please, use one of these types: %(allowed_types)s.") |
|||
type_code = 'invalidType' |
|||
|
|||
oversized_message = _('The current file size is %(size)s. The maximum file size is %(max_size)s.') |
|||
oversized_code = 'oversized' |
|||
|
|||
|
|||
mimetype = mimetypes.guess_type(value.name)[0] |
|||
if self.allowed_types and not (mimetype in self.allowed_types): |
|||
type_params = { |
|||
'type': mimetype, |
|||
'allowed_types': ', '.join(self.allowed_types), |
|||
} |
|||
|
|||
raise ValidationError(type_message, code=type_code, params=type_params) |
|||
|
|||
filesize = len(value) |
|||
if self.max_size and filesize > self.max_size: |
|||
oversized_params = { |
|||
'size': '{}'.format(filesizeformat(filesize)), |
|||
'max_size': '{}'.format(filesizeformat(self.max_size)), |
|||
} |
|||
|
|||
raise ValidationError(oversized_message, code=oversized_code, params=oversized_params) |
|||
@ -0,0 +1,55 @@ |
|||
# -*- mode: python; coding: utf-8 -*- |
|||
"""printer.views |
|||
The views for the printer app |
|||
Author : Maxime Bombar <bombar@crans.org>. |
|||
Date : 29/06/2018 |
|||
""" |
|||
|
|||
from __future__ import unicode_literals |
|||
|
|||
from django.urls import reverse |
|||
from django.shortcuts import render, redirect |
|||
from django.forms import modelformset_factory, formset_factory |
|||
from django.contrib.auth.decorators import login_required |
|||
|
|||
from re2o.views import form |
|||
from users.models import User |
|||
|
|||
from . import settings |
|||
|
|||
from .forms import ( |
|||
JobWithOptionsForm, |
|||
) |
|||
|
|||
@login_required |
|||
def new_job(request): |
|||
""" |
|||
View to create a new printing job |
|||
""" |
|||
job_formset = formset_factory(JobWithOptionsForm)( |
|||
request.POST or None, request.FILES or None, |
|||
) |
|||
if job_formset.is_valid(): |
|||
for job in job_formset: |
|||
job = job.save(commit=False) |
|||
job.user=request.user |
|||
job.status='Printable' |
|||
job.save() |
|||
return redirect(reverse( |
|||
'printer:success', |
|||
)) |
|||
return form( |
|||
{ |
|||
'jobform': job_formset, |
|||
'action_name': "Print", |
|||
}, |
|||
'printer/newjob.html', |
|||
request |
|||
) |
|||
|
|||
def success(request): |
|||
return form( |
|||
{}, |
|||
'printer/success.html', |
|||
request |
|||
) |
|||
@ -0,0 +1,52 @@ |
|||
{% extends "re2o/sidebar.html" %} |
|||
{% comment %} |
|||
Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
se veut agnostique au réseau considéré, de manière à être installable en |
|||
quelques clics. |
|||
|
|||
Copyright © 2017 Gabriel Détraz |
|||
Copyright © 2017 Goulven Kermarec |
|||
Copyright © 2017 Augustin Lemesle |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License along |
|||
with this program; if not, write to the Free Software Foundation, Inc., |
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
{% endcomment %} |
|||
|
|||
{% load bootstrap3 %} |
|||
{% load i18n %} |
|||
|
|||
{% block title %}{% trans "Contact" %}{% endblock %} |
|||
|
|||
{% block content %} |
|||
<h2>{% blocktrans %}Contacter l'association {{asso_name}}{% endblocktrans %}</h2> |
|||
</br> |
|||
|
|||
{% for contact in contacts %} |
|||
|
|||
<div class="panel panel-info"> |
|||
<div class="panel-heading"><h4>{{ contact.get_name }}</h4></div> |
|||
<div class="panel-body"> |
|||
<div class="row"> |
|||
<div class="col-sm-9">{{ contact.commentary}}</div> |
|||
<div class="col-sm-3"><a href="mailto:{{ contact.address }}">{{ contact.address }}</a></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
{% endfor %} |
|||
|
|||
|
|||
|
|||
{% endblock %} |
|||
|
|||
@ -0,0 +1,44 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.10.7 on 2018-06-26 16:37 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
import re2o.mixins |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('machines', '0082_auto_20180525_2209'), |
|||
('topologie', '0060_server'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.CreateModel( |
|||
name='PortProfile', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('name', models.CharField(max_length=255, verbose_name='Name')), |
|||
('profil_default', models.CharField(blank=True, choices=[('room', 'room'), ('nothing', 'nothing'), ('accespoint', 'accesspoint'), ('uplink', 'uplink'), ('asso_machine', 'asso_machine')], max_length=32, null=True, unique=True, verbose_name='profil default')), |
|||
('radius_type', models.CharField(choices=[('NO', 'NO'), ('802.1X', '802.1X'), ('MAC-radius', 'MAC-radius')], max_length=32, verbose_name='RADIUS type')), |
|||
('radius_mode', models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], default='COMMON', max_length=32, verbose_name='RADIUS mode')), |
|||
('speed', models.CharField(choices=[('10-half', '10-half'), ('100-half', '100-half'), ('10-full', '10-full'), ('100-full', '100-full'), ('1000-full', '1000-full'), ('auto', 'auto'), ('auto-10', 'auto-10'), ('auto-100', 'auto-100')], default='auto', help_text='Mode de transmission et vitesse du port', max_length=32, verbose_name='Speed')), |
|||
('mac_limit', models.IntegerField(blank=True, help_text='Limit du nombre de mac sur le port', null=True, verbose_name='Mac limit')), |
|||
('flow_control', models.BooleanField(default=False, help_text='Gestion des débits', verbose_name='Flow control')), |
|||
('dhcp_snooping', models.BooleanField(default=False, help_text='Protection dhcp pirate', verbose_name='Dhcp snooping')), |
|||
('dhcpv6_snooping', models.BooleanField(default=False, help_text='Protection dhcpv6 pirate', verbose_name='Dhcpv6 snooping')), |
|||
('arp_protect', models.BooleanField(default=False, help_text="Verification assignation de l'IP par dhcp", verbose_name='Arp protect')), |
|||
('ra_guard', models.BooleanField(default=False, help_text='Protection contre ra pirate', verbose_name='Ra guard')), |
|||
('loop_protect', models.BooleanField(default=False, help_text='Protection contre les boucles', verbose_name='Loop Protect')), |
|||
('vlan_tagged', models.ManyToManyField(blank=True, related_name='vlan_tagged', to='machines.Vlan', verbose_name='VLAN(s) tagged')), |
|||
('vlan_untagged', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vlan_untagged', to='machines.Vlan', verbose_name='VLAN untagged')), |
|||
], |
|||
options={ |
|||
'verbose_name': 'Port profile', |
|||
'permissions': (('view_port_profile', 'Can view a port profile object'),), |
|||
'verbose_name_plural': 'Port profiles', |
|||
}, |
|||
bases=(re2o.mixins.AclMixin, re2o.mixins.RevMixin, models.Model), |
|||
), |
|||
] |
|||
@ -0,0 +1,25 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.10.7 on 2018-06-26 23:23 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('topologie', '0061_portprofile'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AlterField( |
|||
model_name='portprofile', |
|||
name='radius_mode', |
|||
field=models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], default='COMMON', help_text="En cas d'auth par mac, auth common ou strcit sur le port", max_length=32, verbose_name='RADIUS mode'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='portprofile', |
|||
name='radius_type', |
|||
field=models.CharField(choices=[('NO', 'NO'), ('802.1X', '802.1X'), ('MAC-radius', 'MAC-radius')], help_text="Choix du type d'authentification radius : non actif, mac ou 802.1X", max_length=32, verbose_name='RADIUS type'), |
|||
), |
|||
] |
|||
@ -0,0 +1,21 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.10.7 on 2018-06-28 07:49 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('topologie', '0062_auto_20180627_0123'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AddField( |
|||
model_name='port', |
|||
name='custom_profil', |
|||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='topologie.PortProfile'), |
|||
), |
|||
] |
|||
@ -0,0 +1,53 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.10.7 on 2017-12-31 19:53 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations |
|||
|
|||
|
|||
def transfer_profil(apps, schema_editor): |
|||
db_alias = schema_editor.connection.alias |
|||
port = apps.get_model("topologie", "Port") |
|||
profil = apps.get_model("topologie", "PortProfile") |
|||
vlan = apps.get_model("machines", "Vlan") |
|||
port_list = port.objects.using(db_alias).all() |
|||
profil_nothing = profil.objects.using(db_alias).create(name='nothing', profil_default='nothing', radius_type='NO') |
|||
profil_uplink = profil.objects.using(db_alias).create(name='uplink', profil_default='uplink', radius_type='NO') |
|||
profil_machine = profil.objects.using(db_alias).create(name='asso_machine', profil_default='asso_machine', radius_type='NO') |
|||
profil_room = profil.objects.using(db_alias).create(name='room', profil_default='room', radius_type='NO') |
|||
profil_borne = profil.objects.using(db_alias).create(name='accesspoint', profil_default='accesspoint', radius_type='NO') |
|||
for vlan_instance in vlan.objects.using(db_alias).all(): |
|||
if port.objects.using(db_alias).filter(vlan_force=vlan_instance): |
|||
custom_profil = profil.objects.using(db_alias).create(name='vlan-force-' + str(vlan_instance.vlan_id), radius_type='NO', vlan_untagged=vlan_instance) |
|||
port.objects.using(db_alias).filter(vlan_force=vlan_instance).update(custom_profil=custom_profil) |
|||
if port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').count() > port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').count() and port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').count() > port.objects.using(db_alias).filter(room__isnull=False).filter(radius='COMMON').count(): |
|||
profil_room.radius_type = 'MAC-radius' |
|||
profil_room.radius_mode = 'STRICT' |
|||
common_profil = profil.objects.using(db_alias).create(name='mac-radius-common', radius_type='MAC-radius', radius_mode='COMMON') |
|||
no_rad_profil = profil.objects.using(db_alias).create(name='no-radius', radius_type='NO') |
|||
port.objects.using(db_alias).filter(room__isnull=False).filter(radius='COMMON').update(custom_profil=common_profil) |
|||
port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=no_rad_profil) |
|||
elif port.objects.using(db_alias).filter(room__isnull=False).filter(radius='COMMON').count() > port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').count() and port.objects.using(db_alias).filter(room__isnull=False).filter(radius='COMMON').count() > port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').count(): |
|||
profil_room.radius_type = 'MAC-radius' |
|||
profil_room.radius_mode = 'COMMON' |
|||
strict_profil = profil.objects.using(db_alias).create(name='mac-radius-strict', radius_type='MAC-radius', radius_mode='STRICT') |
|||
no_rad_profil = profil.objects.using(db_alias).create(name='no-radius', radius_type='NO') |
|||
port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').update(custom_profil=strict_profil) |
|||
port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=no_rad_profil) |
|||
else: |
|||
strict_profil = profil.objects.using(db_alias).create(name='mac-radius-strict', radius_type='MAC-radius', radius_mode='STRICT') |
|||
common_profil = profil.objects.using(db_alias).create(name='mac-radius-common', radius_type='MAC-radius', radius_mode='COMMON') |
|||
port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').update(custom_profil=strict_profil) |
|||
port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=common_profil) |
|||
profil_room.save() |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('topologie', '0063_port_custom_profil'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.RunPython(transfer_profil), |
|||
] |
|||
@ -0,0 +1,23 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.10.7 on 2018-06-30 15:03 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('topologie', '0064_createprofil'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.RemoveField( |
|||
model_name='port', |
|||
name='radius', |
|||
), |
|||
migrations.RemoveField( |
|||
model_name='port', |
|||
name='vlan_force', |
|||
), |
|||
] |
|||
@ -0,0 +1,25 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.10.7 on 2018-06-30 16:55 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('topologie', '0065_auto_20180630_1703'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AddField( |
|||
model_name='port', |
|||
name='state', |
|||
field=models.BooleanField(default=True, help_text='Etat du port Actif', verbose_name='Etat du port Actif'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='portprofile', |
|||
name='profil_default', |
|||
field=models.CharField(blank=True, choices=[('room', 'room'), ('accespoint', 'accesspoint'), ('uplink', 'uplink'), ('asso_machine', 'asso_machine'), ('nothing', 'nothing')], max_length=32, null=True, unique=True, verbose_name='profil default'), |
|||
), |
|||
] |
|||
@ -0,0 +1,75 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.10.7 on 2018-06-30 22:16 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('topologie', '0066_auto_20180630_1855'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.RenameField( |
|||
model_name='port', |
|||
old_name='custom_profil', |
|||
new_name='custom_profile', |
|||
), |
|||
migrations.AlterField( |
|||
model_name='port', |
|||
name='state', |
|||
field=models.BooleanField(default=True, help_text='Port state Active', verbose_name='Port State Active'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='portprofile', |
|||
name='arp_protect', |
|||
field=models.BooleanField(default=False, help_text='Check if ip is dhcp assigned', verbose_name='Arp protect'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='portprofile', |
|||
name='dhcp_snooping', |
|||
field=models.BooleanField(default=False, help_text='Protect against rogue dhcp', verbose_name='Dhcp snooping'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='portprofile', |
|||
name='dhcpv6_snooping', |
|||
field=models.BooleanField(default=False, help_text='Protect against rogue dhcpv6', verbose_name='Dhcpv6 snooping'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='portprofile', |
|||
name='flow_control', |
|||
field=models.BooleanField(default=False, help_text='Flow control', verbose_name='Flow control'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='portprofile', |
|||
name='loop_protect', |
|||
field=models.BooleanField(default=False, help_text='Protect again loop', verbose_name='Loop Protect'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='portprofile', |
|||
name='mac_limit', |
|||
field=models.IntegerField(blank=True, help_text='Limit of mac-address on this port', null=True, verbose_name='Mac limit'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='portprofile', |
|||
name='ra_guard', |
|||
field=models.BooleanField(default=False, help_text='Protect against rogue ra', verbose_name='Ra guard'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='portprofile', |
|||
name='radius_mode', |
|||
field=models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], default='COMMON', help_text='In case of mac-auth : mode common or strict on this port', max_length=32, verbose_name='RADIUS mode'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='portprofile', |
|||
name='radius_type', |
|||
field=models.CharField(choices=[('NO', 'NO'), ('802.1X', '802.1X'), ('MAC-radius', 'MAC-radius')], help_text='Type of radius auth : inactive, mac-address or 802.1X', max_length=32, verbose_name='RADIUS type'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='portprofile', |
|||
name='speed', |
|||
field=models.CharField(choices=[('10-half', '10-half'), ('100-half', '100-half'), ('10-full', '10-full'), ('100-full', '100-full'), ('1000-full', '1000-full'), ('auto', 'auto'), ('auto-10', 'auto-10'), ('auto-100', 'auto-100')], default='auto', help_text='Port speed limit', max_length=32, verbose_name='Speed'), |
|||
), |
|||
] |
|||
@ -0,0 +1,85 @@ |
|||
{% comment %} |
|||
Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
se veut agnostique au réseau considéré, de manière à être installable en |
|||
quelques clics. |
|||
|
|||
Copyright © 2018 Gabriel Détraz |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License along |
|||
with this program; if not, write to the Free Software Foundation, Inc., |
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
{% endcomment %} |
|||
|
|||
{% load acl %} |
|||
{% load i18n %} |
|||
|
|||
<div class="table-responsive"> |
|||
|
|||
{% if port_profile_list.paginator %} |
|||
{% include "pagination.html" with list=port_profile_list %} |
|||
{% endif %} |
|||
|
|||
<thead> |
|||
|
|||
<table class="table table-striped"> |
|||
<tr> |
|||
<th>{% trans "Name" %}</th> |
|||
<th>{% trans "Default for" %}</th> |
|||
<th>{% trans "VLANs" %}</th> |
|||
<th>{% trans "RADIUS settings" %}</th> |
|||
<th>{% trans "Speed" %}</th> |
|||
<th>{% trans "Mac address limit" %}</th> |
|||
<th>{% trans "Security" %}</th> |
|||
<th></th> |
|||
</tr> |
|||
</thead> |
|||
{% for port_profile in port_profile_list %} |
|||
<tr> |
|||
<td>{{port_profile.name}}</td> |
|||
<td>{{port_profile.profil_default}}</td> |
|||
<td> |
|||
{% if port_profile.vlan_untagged %} |
|||
<b>Untagged : </b>{{port_profile.vlan_untagged}} |
|||
<br> |
|||
{% endif %} |
|||
{% if port_profile.vlan_tagged.all %} |
|||
<b>Tagged : </b>{{port_profile.vlan_tagged.all|join:", "}} |
|||
{% endif %} |
|||
</td> |
|||
<td> |
|||
<b>Type : </b>{{port_profile.radius_type}} |
|||
{% if port_profile.radius_type == "MAC-radius" %} |
|||
<br> |
|||
<b>Mode : </b>{{port_profile.radius_mode}}</td> |
|||
{% endif %} |
|||
<td>{{port_profile.speed}}</td> |
|||
<td>{{port_profile.mac_limit}}</td> |
|||
<td>{{port_profile.security_parameters_enabled|join:"<br>"}}</td> |
|||
<td class="text-right"> |
|||
{% include 'buttons/history.html' with href='topologie:history' name='portprofile' id=port_profile.pk %} |
|||
{% can_edit port_profile %} |
|||
{% include 'buttons/edit.html' with href='topologie:edit-port-profile' id=port_profile.pk %} |
|||
{% acl_end %} |
|||
{% can_delete port_profile %} |
|||
{% include 'buttons/suppr.html' with href='topologie:del-port-profile' id=port_profile.pk %} |
|||
{% acl_end %} |
|||
</td> |
|||
</tr> |
|||
{% endfor %} |
|||
</table> |
|||
|
|||
{% if port_profile_list.paginator %} |
|||
{% include "pagination.html" with list=port_profile_list %} |
|||
{% endif %} |
|||
|
|||
</div> |
|||
@ -0,0 +1,43 @@ |
|||
{% extends "topologie/sidebar.html" %} |
|||
{% comment %} |
|||
Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
se veut agnostique au réseau considéré, de manière à être installable en |
|||
quelques clics. |
|||
|
|||
Copyright © 2018 Gabriel Détraz |
|||
|
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License along |
|||
with this program; if not, write to the Free Software Foundation, Inc., |
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
{% endcomment %} |
|||
|
|||
{% load bootstrap3 %} |
|||
{% load acl %} |
|||
{% load i18n %} |
|||
|
|||
{% block title %}Switchs{% endblock %} |
|||
|
|||
{% block content %} |
|||
|
|||
<h2>{% trans "Port profiles" %}</h2> |
|||
{% can_create PortProfile %} |
|||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'topologie:new-port-profile' %}"><i class="fa fa-plus"></i>{% trans " Add a port profile" %}</a> |
|||
<hr> |
|||
{% acl_end %} |
|||
{% include "topologie/aff_port_profile.html" with port_profile_list=port_profile_list %} |
|||
<br /> |
|||
<br /> |
|||
<br /> |
|||
|
|||
{% endblock %} |
|||
@ -0,0 +1,46 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.10.7 on 2018-06-29 14:14 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.conf import settings |
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
import re2o.mixins |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('users', '0072_auto_20180426_2021'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.CreateModel( |
|||
name='MailAlias', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('valeur', models.CharField(help_text="username de l'adresse mail", max_length=64, unique=True)), |
|||
], |
|||
bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), |
|||
), |
|||
migrations.RenameField( |
|||
model_name='user', |
|||
old_name='email', |
|||
new_name='external_mail', |
|||
), |
|||
migrations.AddField( |
|||
model_name='user', |
|||
name='internal_address', |
|||
field=models.BooleanField(default=False, help_text="Activer ou non l'utilisation de l'adresse mail interne"), |
|||
), |
|||
migrations.AddField( |
|||
model_name='user', |
|||
name='redirection', |
|||
field=models.BooleanField(default=False, help_text='Activer ou non la redirection du mail interne vers le mail externe'), |
|||
), |
|||
migrations.AddField( |
|||
model_name='mailalias', |
|||
name='user', |
|||
field=models.ForeignKey(blank=True, help_text='Utilisateur associé', null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), |
|||
), |
|||
] |
|||
@ -0,0 +1,41 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.10.7 on 2018-06-29 15:17 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.conf import settings |
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('users', '0073_auto_20180629_1614'), |
|||
] |
|||
|
|||
def transfer_pseudo(apps, schema_editor): |
|||
db_alias = schema_editor.connection.alias |
|||
users = apps.get_model("users", "User") |
|||
mailalias = apps.get_model("users", "MailAlias") |
|||
users_list = users.objects.using(db_alias).all() |
|||
for user in users_list: |
|||
mailalias.objects.using(db_alias).create(valeur=user.pseudo, user=user) |
|||
|
|||
def untransfer_pseudo(apps, schema_editor): |
|||
db_alias = schema_editor.connection.alias |
|||
mailalias = apps.get_model("users", "MailAlias") |
|||
mailalias.objects.using(db_alias).delete() |
|||
|
|||
operations = [ |
|||
migrations.AlterField( |
|||
model_name='mailalias', |
|||
name='user', |
|||
field=models.ForeignKey(help_text='Utilisateur associé', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='mailalias', |
|||
name='valeur', |
|||
field=models.CharField(help_text="Valeur de l'alias mail", max_length=128, unique=True), |
|||
), |
|||
migrations.RunPython(transfer_pseudo, untransfer_pseudo), |
|||
] |
|||
Loading…
Reference in new issue