mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-09-12 01:03:09 +00:00
Merge branch 'fix_stack' into 'master'
Fix stack Closes #104 and #108 See merge request federez/re2o!140
This commit is contained in:
commit
5453b845e2
14 changed files with 2249 additions and 71 deletions
|
@ -27,9 +27,16 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.forms import ModelForm, Form
|
from django.forms import ModelForm, Form
|
||||||
from django import forms
|
from django import forms
|
||||||
from .models import OptionalUser, OptionalMachine, OptionalTopologie
|
from .models import (
|
||||||
from .models import GeneralOption, AssoOption, MailMessageOption, Service
|
OptionalUser,
|
||||||
|
OptionalMachine,
|
||||||
|
OptionalTopologie,
|
||||||
|
GeneralOption,
|
||||||
|
AssoOption,
|
||||||
|
MailMessageOption,
|
||||||
|
AccueilOption,
|
||||||
|
Service
|
||||||
|
)
|
||||||
|
|
||||||
class EditOptionalUserForm(ModelForm):
|
class EditOptionalUserForm(ModelForm):
|
||||||
"""Formulaire d'édition des options de l'user. (solde, telephone..)"""
|
"""Formulaire d'édition des options de l'user. (solde, telephone..)"""
|
||||||
|
@ -185,6 +192,21 @@ class EditMailMessageOptionForm(ModelForm):
|
||||||
mail de bienvenue en anglais'
|
mail de bienvenue en anglais'
|
||||||
|
|
||||||
|
|
||||||
|
class EditAccueilOptionForm(ModelForm):
|
||||||
|
"""Formulaire d'édition des options de la page d'accueil"""
|
||||||
|
class Meta:
|
||||||
|
model = AccueilOption
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||||
|
super(EditAccueilOptionForm, self).__init__(
|
||||||
|
*args,
|
||||||
|
prefix=prefix,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ServiceForm(ModelForm):
|
class ServiceForm(ModelForm):
|
||||||
"""Edition, ajout de services sur la page d'accueil"""
|
"""Edition, ajout de services sur la page d'accueil"""
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
29
preferences/migrations/0033_accueiloption.py
Normal file
29
preferences/migrations/0033_accueiloption.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2018-04-16 02:35
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import re2o.mixins
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('preferences', '0032_optionaluser_shell_default'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='AccueilOption',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('facebook_url', models.URLField(blank=True, help_text='Url du compte facebook', null=True)),
|
||||||
|
('twitter_url', models.URLField(blank=True, help_text='Url du compte twitter', null=True)),
|
||||||
|
('twitter_account_name', models.CharField(blank=True, help_text='Nom du compte à afficher', max_length=32, null=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'permissions': (('view_accueiloption', "Peut voir les options de l'accueil"),),
|
||||||
|
},
|
||||||
|
bases=(re2o.mixins.AclMixin, models.Model),
|
||||||
|
),
|
||||||
|
]
|
|
@ -331,6 +331,40 @@ def assooption_post_save(**kwargs):
|
||||||
asso_pref.set_in_cache()
|
asso_pref.set_in_cache()
|
||||||
|
|
||||||
|
|
||||||
|
class AccueilOption(AclMixin, PreferencesModel):
|
||||||
|
"""Reglages de la page d'accueil"""
|
||||||
|
PRETTY_NAME = "Options de la page d'accueil"
|
||||||
|
|
||||||
|
facebook_url = models.URLField(
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
help_text="Url du compte facebook"
|
||||||
|
)
|
||||||
|
twitter_url = models.URLField(
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
help_text="Url du compte twitter"
|
||||||
|
)
|
||||||
|
twitter_account_name = models.CharField(
|
||||||
|
max_length=32,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
help_text="Nom du compte à afficher"
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
permissions = (
|
||||||
|
("view_accueiloption", "Peut voir les options de l'accueil"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_save, sender=AccueilOption)
|
||||||
|
def accueiloption_post_save(**kwargs):
|
||||||
|
"""Ecriture dans le cache"""
|
||||||
|
accueil_pref = kwargs['instance']
|
||||||
|
accueil_pref.set_in_cache()
|
||||||
|
|
||||||
|
|
||||||
class MailMessageOption(AclMixin, models.Model):
|
class MailMessageOption(AclMixin, models.Model):
|
||||||
"""Reglages, mail de bienvenue et autre"""
|
"""Reglages, mail de bienvenue et autre"""
|
||||||
PRETTY_NAME = "Options de corps de mail"
|
PRETTY_NAME = "Options de corps de mail"
|
||||||
|
|
|
@ -211,12 +211,31 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<td>{{ mailmessageoptions.welcome_mail_en | safe }}</td>
|
<td>{{ mailmessageoptions.welcome_mail_en | safe }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<h2>Liste des services page d'accueil</h2>
|
<h2>Liste des services et préférences page d'accueil</h2>
|
||||||
{% can_create preferences.Service%}
|
{% can_create preferences.Service%}
|
||||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:add-service' %}"><i class="fa fa-plus"></i> Ajouter un service</a>
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:add-service' %}"><i class="fa fa-plus"></i> Ajouter un service</a>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'preferences:del-services' %}"><i class="fa fa-trash"></i> Supprimer un ou plusieurs service</a>
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'preferences:del-services' %}"><i class="fa fa-trash"></i> Supprimer un ou plusieurs service</a>
|
||||||
{% include "preferences/aff_service.html" with service_list=service_list %}
|
{% include "preferences/aff_service.html" with service_list=service_list %}
|
||||||
|
|
||||||
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:edit-options' 'AccueilOption' %}">
|
||||||
|
<i class="fa fa-edit"></i>
|
||||||
|
Editer
|
||||||
|
</a>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
<table class="table table-striped">
|
||||||
|
<tr>
|
||||||
|
<th>Url du compte twitter</th>
|
||||||
|
<td>{{ accueiloptions.twitter_url }}</td>
|
||||||
|
<th>Nom utilisé pour afficher le compte</th>
|
||||||
|
<td>{{ accueiloptions.twitter_account_name }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Url du compte facebook</th>
|
||||||
|
<td>{{ accueiloptions.facebook_url }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
|
|
|
@ -57,6 +57,11 @@ urlpatterns = [
|
||||||
views.edit_options,
|
views.edit_options,
|
||||||
name='edit-options'
|
name='edit-options'
|
||||||
),
|
),
|
||||||
|
url(
|
||||||
|
r'^edit_options/(?P<section>AccueilOption)$',
|
||||||
|
views.edit_options,
|
||||||
|
name='edit-options'
|
||||||
|
),
|
||||||
url(
|
url(
|
||||||
r'^edit_options/(?P<section>MailMessageOption)$',
|
r'^edit_options/(?P<section>MailMessageOption)$',
|
||||||
views.edit_options,
|
views.edit_options,
|
||||||
|
|
|
@ -43,8 +43,16 @@ from re2o.views import form
|
||||||
from re2o.acl import can_create, can_edit, can_delete_set, can_view_all
|
from re2o.acl import can_create, can_edit, can_delete_set, can_view_all
|
||||||
|
|
||||||
from .forms import ServiceForm, DelServiceForm
|
from .forms import ServiceForm, DelServiceForm
|
||||||
from .models import Service, OptionalUser, OptionalMachine, AssoOption
|
from .models import (
|
||||||
from .models import MailMessageOption, GeneralOption, OptionalTopologie
|
Service,
|
||||||
|
OptionalUser,
|
||||||
|
OptionalMachine,
|
||||||
|
AssoOption,
|
||||||
|
MailMessageOption,
|
||||||
|
GeneralOption,
|
||||||
|
OptionalTopologie,
|
||||||
|
AccueilOption
|
||||||
|
)
|
||||||
from . import models
|
from . import models
|
||||||
from . import forms
|
from . import forms
|
||||||
|
|
||||||
|
@ -56,6 +64,7 @@ from . import forms
|
||||||
@can_view_all(GeneralOption)
|
@can_view_all(GeneralOption)
|
||||||
@can_view_all(AssoOption)
|
@can_view_all(AssoOption)
|
||||||
@can_view_all(MailMessageOption)
|
@can_view_all(MailMessageOption)
|
||||||
|
@can_view_all(AccueilOption)
|
||||||
def display_options(request):
|
def display_options(request):
|
||||||
"""Vue pour affichage des options (en vrac) classé selon les models
|
"""Vue pour affichage des options (en vrac) classé selon les models
|
||||||
correspondants dans un tableau"""
|
correspondants dans un tableau"""
|
||||||
|
@ -64,6 +73,7 @@ def display_options(request):
|
||||||
topologieoptions, _created = OptionalTopologie.objects.get_or_create()
|
topologieoptions, _created = OptionalTopologie.objects.get_or_create()
|
||||||
generaloptions, _created = GeneralOption.objects.get_or_create()
|
generaloptions, _created = GeneralOption.objects.get_or_create()
|
||||||
assooptions, _created = AssoOption.objects.get_or_create()
|
assooptions, _created = AssoOption.objects.get_or_create()
|
||||||
|
accueiloptions, _created = AccueilOption.objects.get_or_create()
|
||||||
mailmessageoptions, _created = MailMessageOption.objects.get_or_create()
|
mailmessageoptions, _created = MailMessageOption.objects.get_or_create()
|
||||||
service_list = Service.objects.all()
|
service_list = Service.objects.all()
|
||||||
return form({
|
return form({
|
||||||
|
@ -72,6 +82,7 @@ def display_options(request):
|
||||||
'topologieoptions': topologieoptions,
|
'topologieoptions': topologieoptions,
|
||||||
'generaloptions': generaloptions,
|
'generaloptions': generaloptions,
|
||||||
'assooptions': assooptions,
|
'assooptions': assooptions,
|
||||||
|
'accueiloptions': accueiloptions,
|
||||||
'mailmessageoptions': mailmessageoptions,
|
'mailmessageoptions': mailmessageoptions,
|
||||||
'service_list': service_list
|
'service_list': service_list
|
||||||
}, 'preferences/display_preferences.html', request)
|
}, 'preferences/display_preferences.html', request)
|
||||||
|
|
|
@ -25,4 +25,30 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
|
|
||||||
{% block sidebar %}
|
{% block sidebar %}
|
||||||
|
|
||||||
|
{% if facebook_url %}
|
||||||
|
<div class="fb-page" data-href="{{ facebook_url }}" data-tabs="timeline" data-small-header="false" data-adapt-container-width="true" data-hide-cover="false" data-show-facepile="false"><blockquote cite="{{ facebook_url }}" class="fb-xfbml-parse-ignore"><a href="{{ facebook_url }}">{{ asso_name }}</a></blockquote></div>
|
||||||
|
|
||||||
|
<div id="fb-root"></div>
|
||||||
|
<script>(function(d, s, id) {
|
||||||
|
var js, fjs = d.getElementsByTagName(s)[0];
|
||||||
|
if (d.getElementById(id)) return;
|
||||||
|
js = d.createElement(s); js.id = id;
|
||||||
|
js.src = 'https://connect.facebook.net/fr_FR/sdk.js#xfbml=1&version=v2.12';
|
||||||
|
fjs.parentNode.insertBefore(js, fjs);
|
||||||
|
}(document, 'script', 'facebook-jssdk'));</script>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if twitter_url %}
|
||||||
|
<a class="twitter-timeline" data-lang="fr" data-height="500" href="{{ twitter_url }}?ref_src=twsrc%5Etfw">Tweets de @{{ twitter_account_name }}</a>
|
||||||
|
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
|
||||||
|
|
||||||
|
<a href="{{ twitter_url }}?ref_src=twsrc%5Etfw" class="twitter-follow-button" data-show-count="false"> Suivre @{{ twitter_account_name }}</a>
|
||||||
|
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,12 @@ from django.utils.translation import ugettext as _
|
||||||
from django.views.decorators.cache import cache_page
|
from django.views.decorators.cache import cache_page
|
||||||
|
|
||||||
import preferences
|
import preferences
|
||||||
from preferences.models import Service, GeneralOption, AssoOption
|
from preferences.models import (
|
||||||
|
Service,
|
||||||
|
GeneralOption,
|
||||||
|
AssoOption,
|
||||||
|
AccueilOption
|
||||||
|
)
|
||||||
import users
|
import users
|
||||||
import cotisations
|
import cotisations
|
||||||
import topologie
|
import topologie
|
||||||
|
@ -63,7 +68,17 @@ def index(request):
|
||||||
services = [[], [], []]
|
services = [[], [], []]
|
||||||
for indice, serv in enumerate(Service.objects.all()):
|
for indice, serv in enumerate(Service.objects.all()):
|
||||||
services[indice % 3].append(serv)
|
services[indice % 3].append(serv)
|
||||||
return form({'services_urls': services}, 're2o/index.html', request)
|
twitter_url = AccueilOption.get_cached_value('twitter_url')
|
||||||
|
facebook_url = AccueilOption.get_cached_value('facebook_url')
|
||||||
|
twitter_account_name = AccueilOption.get_cached_value('twitter_account_name')
|
||||||
|
asso_name = AssoOption.get_cached_value('pseudo')
|
||||||
|
return form({
|
||||||
|
'services_urls': services,
|
||||||
|
'twitter_url': twitter_url,
|
||||||
|
'twitter_account_name' : twitter_account_name,
|
||||||
|
'facebook_url': facebook_url,
|
||||||
|
'asso_name': asso_name
|
||||||
|
}, 're2o/index.html', request)
|
||||||
|
|
||||||
|
|
||||||
#: Binding the corresponding char sequence of history url to re2o models.
|
#: Binding the corresponding char sequence of history url to re2o models.
|
||||||
|
|
1929
static/js/jquery.ez-plus.js
Normal file
1929
static/js/jquery.ez-plus.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -31,58 +31,31 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<th>{% include "buttons/sort.html" with prefix='stack' col='id' text='ID' %}</th>
|
<th>{% include "buttons/sort.html" with prefix='stack' col='id' text='ID' %}</th>
|
||||||
<th>Détails</th>
|
<th>Détails</th>
|
||||||
<th>Membres</th>
|
<th>Membres</th>
|
||||||
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
{% for stack in stack_list %}
|
{% for stack in stack_list %}
|
||||||
{% for switch in stack.switch_set.all %}
|
<tr>
|
||||||
<tbody>
|
<td>{{ stack.name }}</td>
|
||||||
<tr class="active">
|
<td>{{stack.stack_id}}</td>
|
||||||
{% if forloop.first %}
|
<td>{{stack.details}}</td>
|
||||||
<td rowspan="{{ stack.switch_set.all|length }}">{{stack.name}}</td>
|
<td>{% for switch in stack.switch_set.all %}<a href="{% url 'topologie:index-port' switch.pk %}">{{switch }} </a>{% endfor %}</td>
|
||||||
<td rowspan="{{ stack.switch_set.all|length }}">{{stack.stack_id}}</td>
|
<td class="text-right">
|
||||||
<td rowspan="{{ stack.switch_set.all|length }}" >{{stack.details}}</td>
|
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'stack' stack.pk %}">
|
||||||
{% endif %}
|
<i class="fa fa-history"></i>
|
||||||
<td><a href="{% url 'topologie:index-port' switch.pk %}">{{switch}}</a></td>
|
</a>
|
||||||
{% if forloop.first %}
|
{% can_edit stack %}
|
||||||
<td rowspan="{{ stack.switch_set.all|length }}">
|
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'topologie:edit-stack' stack.id %}">
|
||||||
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'stack' stack.pk %}">
|
<i class="fa fa-edit"></i>
|
||||||
<i class="fa fa-history"></i>
|
</a>
|
||||||
</a>
|
{% acl_end %}
|
||||||
{% can_edit stack %}
|
{% can_delete stack %}
|
||||||
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'topologie:edit-stack' stack.id %}">
|
<a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-stack' stack.pk %}">
|
||||||
<i class="fa fa-edit"></i>
|
<i class="fa fa-trash"></i>
|
||||||
</a>
|
</a>
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
{% can_delete stack %}
|
</td>
|
||||||
<a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-stack' stack.pk %}">
|
</tr>
|
||||||
<i class="fa fa-trash"></i>
|
{% endfor %}
|
||||||
</a>
|
|
||||||
{% acl_end %}
|
|
||||||
</td>
|
|
||||||
{% endif %}
|
|
||||||
</tr>
|
|
||||||
{% empty %}
|
|
||||||
<tr class="active">
|
|
||||||
<td>{{stack.name}}</td>
|
|
||||||
<td>{{stack.stack_id}}</td>
|
|
||||||
<td>{{stack.details}}</td>
|
|
||||||
<td>Aucun</td>
|
|
||||||
<td>
|
|
||||||
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'stack' stack.pk %}">
|
|
||||||
<i class="fa fa-history"></i>
|
|
||||||
</a>
|
|
||||||
{% can_edit stack %}
|
|
||||||
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'topologie:edit-stack' stack.id %}">
|
|
||||||
<i class="fa fa-edit"></i>
|
|
||||||
</a>
|
|
||||||
{% acl_end %}
|
|
||||||
{% can_delete stack %}
|
|
||||||
<a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-stack' stack.pk %}">
|
|
||||||
<i class="fa fa-trash"></i>
|
|
||||||
</a>
|
|
||||||
{% acl_end %}
|
|
||||||
</td>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
|
@ -24,11 +24,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
{% load acl %}
|
{% load acl %}
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
{% if switch_list.paginator %}
|
{% if switch_list.paginator %}
|
||||||
{% include "pagination.html" with list=switch_list %}
|
{% include "pagination.html" with list=switch_list %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -72,8 +73,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if switch_list.paginator %}
|
{% if switch_list.paginator %}
|
||||||
{% include "pagination.html" with list=switch_list %}
|
{% include "pagination.html" with list=switch_list %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
|
@ -43,7 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<td>{{switch_bay.name}}</td>
|
<td>{{switch_bay.name}}</td>
|
||||||
<td>{{switch_bay.building}}</td>
|
<td>{{switch_bay.building}}</td>
|
||||||
<td>{{switch_bay.info}}</td>
|
<td>{{switch_bay.info}}</td>
|
||||||
<td>{{switch_bay.switch_set.all |join:", "}}</td>
|
<td>{% for switch in switch_bay.switch_set.all %}<a href="{% url 'topologie:index-port' switch.pk %}">{{switch }} </a>{% endfor %}</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'switchbay' switch_bay.pk %}">
|
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'switchbay' switch_bay.pk %}">
|
||||||
<i class="fa fa-history"></i>
|
<i class="fa fa-history"></i>
|
||||||
|
|
|
@ -29,7 +29,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% block title %}Switchs{% endblock %}
|
{% block title %}Switchs{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Switchs</h2>
|
<img id="zoom_01" src="/media/images/switchs.png" data-zoom-image="/media/images/switchs.png" width=100% />
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/static/js/jquery.ez-plus.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$("#zoom_01").ezPlus({
|
||||||
|
scrollZoom: true,
|
||||||
|
zoomType: 'inner',
|
||||||
|
cursor: 'crosshair'
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h2>Switchs</h2>
|
||||||
{% can_create Switch %}
|
{% can_create Switch %}
|
||||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'topologie:new-switch' %}"><i class="fa fa-plus"></i> Ajouter un switch</a>
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'topologie:new-switch' %}"><i class="fa fa-plus"></i> Ajouter un switch</a>
|
||||||
<hr>
|
<hr>
|
||||||
|
@ -38,4 +50,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -42,6 +42,7 @@ from django.contrib.auth.decorators import login_required
|
||||||
from django.db import IntegrityError
|
from django.db import IntegrityError
|
||||||
from django.db.models import ProtectedError, Prefetch
|
from django.db.models import ProtectedError, Prefetch
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.contrib.staticfiles.storage import staticfiles_storage
|
||||||
|
|
||||||
from users.views import form
|
from users.views import form
|
||||||
from re2o.utils import re2o_paginator, SortTable
|
from re2o.utils import re2o_paginator, SortTable
|
||||||
|
@ -88,6 +89,8 @@ from .forms import (
|
||||||
EditBuildingForm
|
EditBuildingForm
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from subprocess import Popen,PIPE
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_view_all(Switch)
|
@can_view_all(Switch)
|
||||||
|
@ -349,6 +352,7 @@ def new_stack(request):
|
||||||
if stack.is_valid():
|
if stack.is_valid():
|
||||||
stack.save()
|
stack.save()
|
||||||
messages.success(request, "Stack crée")
|
messages.success(request, "Stack crée")
|
||||||
|
return redirect(reverse('topologie:index-physical-grouping'))
|
||||||
return form(
|
return form(
|
||||||
{'topoform': stack, 'action_name': 'Créer'},
|
{'topoform': stack, 'action_name': 'Créer'},
|
||||||
'topologie/topo.html',
|
'topologie/topo.html',
|
||||||
|
@ -364,7 +368,7 @@ def edit_stack(request, stack, **_kwargs):
|
||||||
if stack.is_valid():
|
if stack.is_valid():
|
||||||
if stack.changed_data:
|
if stack.changed_data:
|
||||||
stack.save()
|
stack.save()
|
||||||
return redirect(reverse('topologie:index-physical-grouping'))
|
return redirect(reverse('topologie:index-physical-grouping'))
|
||||||
return form(
|
return form(
|
||||||
{'topoform': stack, 'action_name': 'Editer'},
|
{'topoform': stack, 'action_name': 'Editer'},
|
||||||
'topologie/topo.html',
|
'topologie/topo.html',
|
||||||
|
@ -926,8 +930,103 @@ def del_constructor_switch(request, constructor_switch, **_kwargs):
|
||||||
"de la supprimer (switch ou user)" % constructor_switch)
|
"de la supprimer (switch ou user)" % constructor_switch)
|
||||||
)
|
)
|
||||||
return redirect(reverse('topologie:index-model-switch'))
|
return redirect(reverse('topologie:index-model-switch'))
|
||||||
return form(
|
return form({
|
||||||
{'objet': constructor_switch, 'objet_name': 'Constructeur de switch'},
|
'objet': constructor_switch,
|
||||||
'topologie/delete.html',
|
'objet_name': 'Constructeur de switch'
|
||||||
request
|
}, 'topologie/delete.html', request)
|
||||||
)
|
|
||||||
|
|
||||||
|
def make_machine_graph():
|
||||||
|
"""
|
||||||
|
Crée le fichier dot et l'image du graph des Switchs
|
||||||
|
"""
|
||||||
|
#Syntaxe DOT temporaire, A mettre dans un template:
|
||||||
|
lignes=['''digraph Switchs {
|
||||||
|
node [
|
||||||
|
fontname=Helvetica
|
||||||
|
fontsize=8
|
||||||
|
shape=plaintext]
|
||||||
|
edge[arrowhead=odot,arrowtail=dot]''']
|
||||||
|
node_fixe='''node [label=<
|
||||||
|
<TABLE BGCOLOR="palegoldenrod" BORDER="0" CELLBORDER="0" CELLSPACING="0">
|
||||||
|
<TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="olivedrab4">
|
||||||
|
<FONT FACE="Helvetica Bold" COLOR="white">
|
||||||
|
{}
|
||||||
|
</FONT></TD></TR>
|
||||||
|
<TR><TD ALIGN="LEFT" BORDER="0">
|
||||||
|
<FONT COLOR="#7B7B7B" >{}</FONT>
|
||||||
|
</TD>
|
||||||
|
<TD ALIGN="LEFT">
|
||||||
|
<FONT COLOR="#7B7B7B" >{}</FONT>
|
||||||
|
</TD></TR>
|
||||||
|
<TR><TD ALIGN="LEFT" BORDER="0">
|
||||||
|
<FONT COLOR="#7B7B7B" >{}</FONT>
|
||||||
|
</TD>
|
||||||
|
<TD ALIGN="LEFT">
|
||||||
|
<FONT>{}</FONT>
|
||||||
|
</TD></TR>'''
|
||||||
|
node_ports='''<TR><TD ALIGN="LEFT" BORDER="0">
|
||||||
|
<FONT COLOR="#7B7B7B" >{}</FONT>
|
||||||
|
</TD>
|
||||||
|
<TD ALIGN="LEFT">
|
||||||
|
<FONT>{}</FONT>
|
||||||
|
</TD></TR>'''
|
||||||
|
cluster='''subgraph cluster_{} {{
|
||||||
|
color=blue;
|
||||||
|
label="Batiment {}";'''
|
||||||
|
end_table='''</TABLE>
|
||||||
|
>] \"{}_{}\" ;'''
|
||||||
|
switch_alone='''{} [label=<
|
||||||
|
<TABLE BGCOLOR="palegoldenrod" BORDER="0" CELLBORDER="0" CELLSPACING="0">
|
||||||
|
<TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="olivedrab4">
|
||||||
|
<FONT FACE="Helvetica Bold" COLOR="white">
|
||||||
|
{}
|
||||||
|
</FONT></TD></TR>
|
||||||
|
</TABLE>
|
||||||
|
>]'''
|
||||||
|
missing=[]
|
||||||
|
detected=[]
|
||||||
|
for sw in Switch.objects.all():
|
||||||
|
if(sw not in detected):
|
||||||
|
missing.append(sw)
|
||||||
|
for building in Building.objects.all():
|
||||||
|
lignes.append(cluster.format(len(lignes),building))
|
||||||
|
for switch in Switch.objects.filter(switchbay__building=building):
|
||||||
|
lignes.append(node_fixe.format(switch.main_interface().domain.name,"Modèle",switch.model,"Nombre de ports",switch.number))
|
||||||
|
for p in switch.ports.all().filter(related__isnull=False):
|
||||||
|
lignes.append(node_ports.format(p.port,p.related.switch.main_interface().domain.name))
|
||||||
|
lignes.append(end_table.format(building.id,switch.id))
|
||||||
|
lignes.append("}")
|
||||||
|
while(missing!=[]):
|
||||||
|
lignes,new_detected=recursive_switchs(missing[0].ports.all().filter(related=None).first(),None,lignes,[missing[0]])
|
||||||
|
missing=[i for i in missing if i not in new_detected]
|
||||||
|
detected+=new_detected
|
||||||
|
for switch in Switch.objects.all().filter(switchbay__isnull=True).exclude(ports__related__isnull=False):
|
||||||
|
lignes.append(switch_alone.format(switch.id,switch.main_interface().domain.name))
|
||||||
|
lignes.append("}")
|
||||||
|
fichier = open("media/images/switchs.dot","w")
|
||||||
|
for ligne in lignes:
|
||||||
|
fichier.write(ligne+"\n")
|
||||||
|
fichier.close()
|
||||||
|
unflatten = Popen(["unflatten","-l", "3", "media/images/switchs.dot"], stdout=PIPE)
|
||||||
|
image = Popen(["dot", "-Tpng", "-o", "media/images/switchs.png"], stdin=unflatten.stdout, stdout=PIPE)
|
||||||
|
|
||||||
|
|
||||||
|
def recursive_switchs(port_start, switch_before, lignes,detected):
|
||||||
|
"""
|
||||||
|
Parcour récursivement le switchs auquel appartient port_start pour trouver les ports suivants liés
|
||||||
|
"""
|
||||||
|
l_ports=port_start.switch.ports.filter(related__isnull=False)
|
||||||
|
for port in l_ports:
|
||||||
|
if port.related.switch!=switch_before and port.related.switch!=port.switch:
|
||||||
|
links=[]
|
||||||
|
for sw in [switch for switch in [port_start.switch,port.related.switch]]:
|
||||||
|
if(sw not in detected):
|
||||||
|
detected.append(sw)
|
||||||
|
if(sw.switchbay.building):
|
||||||
|
links.append("\"{}_{}\"".format(sw.switchbay.building.id,sw.id))
|
||||||
|
else:
|
||||||
|
links.append("\"{}\"".format(sw.id))
|
||||||
|
lignes.append(links[0]+" -> "+links[1])
|
||||||
|
lignes, detected = recursive_switchs(port.related, port_start.switch, lignes, detected)
|
||||||
|
return (lignes, detected)
|
||||||
|
|
Loading…
Reference in a new issue