mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-08-20 06:03:40 +00:00
Merge branch 'master' into reverse_url
This commit is contained in:
commit
1f293e99d7
15 changed files with 593 additions and 204 deletions
21
.gitlab-ci.yml
Normal file
21
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
image: debian:stretch
|
||||
stages:
|
||||
- lint
|
||||
|
||||
lint:
|
||||
stage: lint
|
||||
variables:
|
||||
LANG: 'en_US.UTF-8'
|
||||
LC_ALL: 'en_US.UTF-8'
|
||||
LANGUAGE: 'en_US.UTF-8'
|
||||
script:
|
||||
- apt-get -qq update
|
||||
- DEBIAN_FRONTEND=noninteractive apt-get -qq install -y locales python3-pip python3-django
|
||||
- sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && dpkg-reconfigure --frontend=noninteractive locales && update-locale LANG=en_US.UTF-8
|
||||
- pip3 install -q pylint-django
|
||||
- pylint --load-plugins pylint_django cotisations machines re2o logs topologie preferences search users || if [ $? -ne 1 ]; then exit 0; else exit 1; fi
|
||||
|
||||
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ dessus, qui accèdent à la base de donnée en passant par django (ex : dhcp), e
|
|||
chargeant la liste de toutes les mac-ip, ou la liste des mac-ip autorisées sur
|
||||
le réseau (adhérent à jour de cotisation).
|
||||
|
||||
#Installation
|
||||
# Installation
|
||||
|
||||
## Installation des dépendances
|
||||
|
||||
|
|
|
@ -56,8 +56,10 @@ class NewFactureForm(ModelForm):
|
|||
self.fields['banque'].empty_label = "Non renseigné"
|
||||
self.fields['paiement'].empty_label = "Séléctionner\
|
||||
un moyen de paiement"
|
||||
self.fields['paiement'].widget.attrs['data-cheque'] = Paiement.objects\
|
||||
.filter(type_paiement=1).first().id
|
||||
paiement_list = Paiement.objects.filter(type_paiement=1)
|
||||
if paiement_list:
|
||||
self.fields['paiement'].widget\
|
||||
.attrs['data-cheque'] = paiement_list.first().id
|
||||
|
||||
class Meta:
|
||||
model = Facture
|
||||
|
|
|
@ -154,12 +154,13 @@ def authorize(data):
|
|||
else:
|
||||
nas_type = None
|
||||
if not nas_type or nas_type.port_access_mode == '802.1X':
|
||||
user = data.get('User-Name', '')
|
||||
user = data.get('User-Name', '').decode('utf-8', errors='replace')
|
||||
user = user.split('@', 1)[0]
|
||||
mac = data.get('Calling-Station-Id', '')
|
||||
result, log, password = check_user_machine_and_register(nas_type, user, mac)
|
||||
logger.info(log.encode('utf-8'))
|
||||
|
||||
logger.info(user.encode('utf-8'))
|
||||
|
||||
if not result:
|
||||
return radiusd.RLM_MODULE_REJECT
|
||||
else:
|
||||
|
|
|
@ -36,7 +36,9 @@ def url_insert_param(url="", **kwargs):
|
|||
Return the URL with some specific parameters inserted into the query
|
||||
part. If a URL has already some parameters, those requested will be
|
||||
modified if already exisiting or will be added and the other parameters
|
||||
will stay unmodified.
|
||||
will stay unmodified. If parameters with the same name are already in the
|
||||
URL and a value is specified for this parameter, it will replace all
|
||||
existing parameters.
|
||||
|
||||
**Tag name**::
|
||||
|
||||
|
@ -82,19 +84,22 @@ def url_insert_param(url="", **kwargs):
|
|||
# Get existing parameters in the url
|
||||
params = {}
|
||||
if '?' in url:
|
||||
url, params = url.split('?', maxsplit=1)
|
||||
params = {
|
||||
p[:p.find('=')]: p[p.find('=')+1:] for p in params.split('&')
|
||||
}
|
||||
url, parameters = url.split('?', maxsplit=1)
|
||||
for parameter in parameters.split('&'):
|
||||
p_name, p_value = parameter.split('=', maxsplit=1)
|
||||
if p_name not in params:
|
||||
params[p_name] = []
|
||||
params[p_name].append(p_value)
|
||||
|
||||
# Add the request parameters to the list of parameters
|
||||
for key, value in kwargs.items():
|
||||
params[key] = value
|
||||
params[key] = [value]
|
||||
|
||||
# Write the url
|
||||
url += '?'
|
||||
for param, value in params.items():
|
||||
url += str(param) + '=' + str(value) + '&'
|
||||
for param, value_list in params.items():
|
||||
for value in value_list:
|
||||
url += str(param) + '=' + str(value) + '&'
|
||||
|
||||
# Remove the last '&' (or '?' if no parameters)
|
||||
return url[:-1]
|
||||
|
|
|
@ -248,7 +248,7 @@ class SortTable:
|
|||
if not fields:
|
||||
fields = values.get('default', [])
|
||||
request = request.order_by(*fields)
|
||||
if order == 'desc':
|
||||
if values.get(col, None) and order == 'desc':
|
||||
return request.reverse()
|
||||
else:
|
||||
return request
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
"""The field used in the admin view for the search app"""
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
|
|
|
@ -20,21 +20,83 @@
|
|||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
"""The forms used by the search app"""
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db.models import Q
|
||||
from simple_search import BaseSearchForm
|
||||
from django import forms
|
||||
from django.forms import Form
|
||||
|
||||
from users.models import User, School
|
||||
CHOICES_USER = (
|
||||
('0', 'Actifs'),
|
||||
('1', 'Désactivés'),
|
||||
('2', 'Archivés'),
|
||||
)
|
||||
|
||||
class UserSearchForm(BaseSearchForm):
|
||||
class Meta:
|
||||
base_qs = User.objects
|
||||
search_fields = ('^name', 'description', 'specifications', '=id')
|
||||
CHOICES_AFF = (
|
||||
('0', 'Utilisateurs'),
|
||||
('1', 'Machines'),
|
||||
('2', 'Factures'),
|
||||
('3', 'Bannissements'),
|
||||
('4', 'Accès à titre gracieux'),
|
||||
('5', 'Chambres'),
|
||||
('6', 'Ports'),
|
||||
('7', 'Switchs'),
|
||||
)
|
||||
|
||||
# assumes a fulltext index has been defined on the fields
|
||||
# 'name,description,specifications,id'
|
||||
fulltext_indexes = (
|
||||
('name', 2), # name matches are weighted higher
|
||||
('name,description,specifications,id', 1),
|
||||
)
|
||||
|
||||
def initial_choices(choice_set):
|
||||
"""Return the choices that should be activated by default for a
|
||||
given set of choices"""
|
||||
return [i[0] for i in choice_set]
|
||||
|
||||
|
||||
class SearchForm(Form):
|
||||
"""The form for a simple search"""
|
||||
q = forms.CharField(
|
||||
label='Recherche',
|
||||
help_text=(
|
||||
'Utilisez « » et «,» pour spécifier différents mots, «"query"» '
|
||||
'pour une recherche exacte et «\\» pour échapper un caractère.'
|
||||
),
|
||||
max_length=100
|
||||
)
|
||||
|
||||
|
||||
class SearchFormPlus(Form):
|
||||
"""The form for an advanced search (with filters)"""
|
||||
q = forms.CharField(
|
||||
label='Recherche',
|
||||
help_text=(
|
||||
'Utilisez « » et «,» pour spécifier différents mots, «"query"» '
|
||||
'pour une recherche exacte et «\\» pour échapper un caractère.'
|
||||
),
|
||||
max_length=100,
|
||||
required=False
|
||||
)
|
||||
u = forms.MultipleChoiceField(
|
||||
label="Filtre utilisateurs",
|
||||
required=False,
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
choices=CHOICES_USER,
|
||||
initial=initial_choices(CHOICES_USER)
|
||||
)
|
||||
a = forms.MultipleChoiceField(
|
||||
label="Filtre affichage",
|
||||
required=False,
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
choices=CHOICES_AFF,
|
||||
initial=initial_choices(CHOICES_AFF)
|
||||
)
|
||||
s = forms.DateField(
|
||||
required=False,
|
||||
label="Date de début",
|
||||
help_text='DD/MM/YYYY',
|
||||
input_formats=['%d/%m/%Y']
|
||||
)
|
||||
e = forms.DateField(
|
||||
required=False,
|
||||
help_text='DD/MM/YYYY',
|
||||
input_formats=['%d/%m/%Y'],
|
||||
label="Date de fin"
|
||||
)
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
# -*- mode: python; coding: utf-8 -*-
|
||||
# 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.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models
|
||||
from django import forms
|
||||
from django.forms import Form
|
||||
from django.forms import ModelForm
|
||||
|
||||
CHOICES = (
|
||||
('0', 'Actifs'),
|
||||
('1', 'Désactivés'),
|
||||
('2', 'Archivés'),
|
||||
)
|
||||
|
||||
CHOICES2 = (
|
||||
(1, 'Active'),
|
||||
("", 'Désactivée'),
|
||||
)
|
||||
|
||||
CHOICES3 = (
|
||||
('0', 'Utilisateurs'),
|
||||
('1', 'Machines'),
|
||||
('2', 'Factures'),
|
||||
('3', 'Bannissements'),
|
||||
('4', 'Accès à titre gracieux'),
|
||||
('6', 'Switchs'),
|
||||
('5', 'Ports'),
|
||||
)
|
||||
|
||||
|
||||
class SearchForm(Form):
|
||||
search_field = forms.CharField(label = 'Search', max_length = 100)
|
||||
|
||||
class SearchFormPlus(Form):
|
||||
search_field = forms.CharField(label = 'Search', max_length = 100, required=False)
|
||||
filtre = forms.MultipleChoiceField(label="Filtre utilisateurs", required=False, widget =forms.CheckboxSelectMultiple,choices=CHOICES)
|
||||
connexion = forms.MultipleChoiceField(label="Filtre connexion", required=False, widget =forms.CheckboxSelectMultiple,choices=CHOICES2)
|
||||
affichage = forms.MultipleChoiceField(label="Filtre affichage", required=False, widget =forms.CheckboxSelectMultiple,choices=CHOICES3)
|
||||
date_deb = forms.DateField(required=False, label="Date de début", help_text='DD/MM/YYYY', input_formats=['%d/%m/%Y'])
|
||||
date_fin = forms.DateField(required=False, help_text='DD/MM/YYYY', input_formats=['%d/%m/%Y'], label="Date de fin")
|
|
@ -28,38 +28,43 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% block title %}Résultats de la recherche{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if users_list %}
|
||||
{% if users %}
|
||||
<h2>Résultats dans les utilisateurs</h2>
|
||||
{% include "users/aff_users.html" with users_list=users_list %}
|
||||
{% include "users/aff_users.html" with users_list=users %}
|
||||
{% endif%}
|
||||
{% if machines_list %}
|
||||
{% if machines %}
|
||||
<h2>Résultats dans les machines : </h2>
|
||||
{% include "machines/aff_machines.html" with machines_list=machines_list %}
|
||||
{% include "machines/aff_machines.html" with machines_list=machines %}
|
||||
{% endif %}
|
||||
{% if facture_list %}
|
||||
{% if factures %}
|
||||
<h2>Résultats dans les factures : </h2>
|
||||
{% include "cotisations/aff_cotisations.html" with facture_list=facture_list %}
|
||||
{% include "cotisations/aff_cotisations.html" with facture_list=factures %}
|
||||
{% endif %}
|
||||
{% if white_list %}
|
||||
{% if whitelists %}
|
||||
<h2>Résultats dans les accès à titre gracieux : </h2>
|
||||
{% include "users/aff_whitelists.html" with white_list=white_list %}
|
||||
{% include "users/aff_whitelists.html" with white_list=whitelists %}
|
||||
{% endif %}
|
||||
{% if ban_list %}
|
||||
{% if bans %}
|
||||
<h2>Résultats dans les banissements : </h2>
|
||||
{% include "users/aff_bans.html" with ban_list=ban_list %}
|
||||
{% include "users/aff_bans.html" with ban_list=bans %}
|
||||
{% endif %}
|
||||
{% if switch_list %}
|
||||
<h2>Résultats dans les switchs : </h2>
|
||||
{% include "topologie/aff_switch.html" with switch_list=switch_list %}
|
||||
{% if rooms %}
|
||||
<h2>Résultats dans les chambres : </h2>
|
||||
{% include "topologie/aff_chambres.html" with room_list=rooms %}
|
||||
{% endif %}
|
||||
{% if port_list %}
|
||||
{% if ports %}
|
||||
<h2>Résultats dans les ports : </h2>
|
||||
{% include "topologie/aff_port.html" with port_list=port_list %}
|
||||
{% include "topologie/aff_port.html" with port_list=ports %}
|
||||
{% endif %}
|
||||
{% if not ban_list and not interfaces_list and not users_list and not facture_list and not white_list and not port_list and not switch_list%}
|
||||
{% if switches %}
|
||||
<h2>Résultats dans les switchs : </h2>
|
||||
{% include "topologie/aff_switch.html" with switch_list=switches %}
|
||||
{% endif %}
|
||||
{% if not users and not machines and not factures and not whitelists and not bans and not rooms and not ports and not switches %}
|
||||
<h3>Aucun résultat</h3>
|
||||
{% else %}
|
||||
<h6>(Seulement les {{ max_result }} premiers résultats sont affichés dans chaque catégorie)</h6>
|
||||
{% endif %}
|
||||
<h6>(Seulement les {{ max_result }} premiers résultats sont affichés dans chaque catégorie)</h6>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -28,11 +28,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% block title %}Recherche{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% bootstrap_form_errors searchform %}
|
||||
{% bootstrap_form_errors search_form %}
|
||||
|
||||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form searchform %}
|
||||
<form class="form">
|
||||
{% bootstrap_field search_form.q %}
|
||||
{% if search_form.u %}
|
||||
{% include "buttons/multiple_checkbox_alt.html" with field=search_form.u %}
|
||||
{% endif %}
|
||||
{% if search_form.a %}
|
||||
{% include "buttons/multiple_checkbox_alt.html" with field=search_form.a %}
|
||||
{% endif %}
|
||||
{% if search_form.s %}
|
||||
{% bootstrap_field search_form.s %}
|
||||
{% endif %}
|
||||
{% if search_form.e %}
|
||||
{% bootstrap_field search_form.e %}
|
||||
{% endif %}
|
||||
{% bootstrap_button "Search" button_type="submit" icon="search" %}
|
||||
</form>
|
||||
<br />
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
"""The urls used by the search app"""
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf.urls import url
|
||||
|
@ -28,5 +30,5 @@ from . import views
|
|||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.search, name='search'),
|
||||
url(r'^avance/$', views.searchp, name='searchp'),
|
||||
url(r'^advanced/$', views.searchp, name='searchp'),
|
||||
]
|
||||
|
|
477
search/views.py
477
search/views.py
|
@ -20,115 +20,418 @@
|
|||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
# App de recherche pour re2o
|
||||
# Augustin lemesle, Gabriel Détraz, Goulven Kermarec
|
||||
# Gplv2
|
||||
"""The views for the search app, responsible for finding the matches
|
||||
Augustin lemesle, Gabriel Détraz, Goulven Kermarec, Maël Kervella
|
||||
Gplv2"""
|
||||
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.shortcuts import render
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.template.context_processors import csrf
|
||||
from django.template import Context, RequestContext, loader
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
||||
from django.db.models import Q
|
||||
from users.models import User, Ban, Whitelist
|
||||
from machines.models import Machine, Interface
|
||||
from topologie.models import Port, Switch
|
||||
from machines.models import Machine
|
||||
from topologie.models import Port, Switch, Room
|
||||
from cotisations.models import Facture
|
||||
from search.models import SearchForm, SearchFormPlus
|
||||
from preferences.models import GeneralOption
|
||||
from search.forms import (
|
||||
SearchForm,
|
||||
SearchFormPlus,
|
||||
CHOICES_USER,
|
||||
CHOICES_AFF,
|
||||
initial_choices
|
||||
)
|
||||
from re2o.utils import SortTable
|
||||
|
||||
def form(ctx, template, request):
|
||||
c = ctx
|
||||
c.update(csrf(request))
|
||||
return render(request, template, c)
|
||||
|
||||
def search_result(search, type, request):
|
||||
date_deb = None
|
||||
date_fin = None
|
||||
states=[]
|
||||
co=[]
|
||||
aff=[]
|
||||
if(type):
|
||||
aff = search.cleaned_data['affichage']
|
||||
co = search.cleaned_data['connexion']
|
||||
states = search.cleaned_data['filtre']
|
||||
date_deb = search.cleaned_data['date_deb']
|
||||
date_fin = search.cleaned_data['date_fin']
|
||||
date_query = Q()
|
||||
if aff==[]:
|
||||
aff = ['0','1','2','3','4','5','6']
|
||||
if date_deb != None:
|
||||
date_query = date_query & Q(date__gte=date_deb)
|
||||
if date_fin != None:
|
||||
date_query = date_query & Q(date__lte=date_fin)
|
||||
search = search.cleaned_data['search_field']
|
||||
query1 = Q()
|
||||
for s in states:
|
||||
query1 = query1 | Q(state = s)
|
||||
|
||||
connexion = []
|
||||
|
||||
recherche = {'users_list': None, 'machines_list' : [], 'facture_list' : None, 'ban_list' : None, 'white_list': None, 'port_list': None, 'switch_list': None}
|
||||
def is_int(variable):
|
||||
""" Check if the variable can be casted to an integer """
|
||||
|
||||
if request.user.has_perms(('cableur',)):
|
||||
query = Q(user__pseudo__icontains = search) | Q(user__adherent__name__icontains = search) | Q(user__surname__icontains = search)
|
||||
try:
|
||||
int(variable)
|
||||
except ValueError:
|
||||
return False
|
||||
else:
|
||||
query = (Q(user__pseudo__icontains = search) | Q(user__adherent__name__icontains = search) | Q(user__surname__icontains = search)) & Q(user = request.user)
|
||||
return True
|
||||
|
||||
|
||||
for i in aff:
|
||||
if i == '0':
|
||||
query_user_list = Q(adherent__room__name__icontains = search) | Q(club__room__name__icontains = search) | Q(pseudo__icontains = search) | Q(adherent__name__icontains = search) | Q(surname__icontains = search) & query1
|
||||
if request.user.has_perms(('cableur',)):
|
||||
recherche['users_list'] = User.objects.filter(query_user_list).order_by('state', 'surname').distinct()
|
||||
else :
|
||||
recherche['users_list'] = User.objects.filter(query_user_list & Q(id=request.user.id)).order_by('state', 'surname').distinct()
|
||||
if i == '1':
|
||||
query_machine_list = Q(machine__user__pseudo__icontains = search) | Q(machine__user__adherent__name__icontains = search) | Q(machine__user__surname__icontains = search) | Q(mac_address__icontains = search) | Q(ipv4__ipv4__icontains = search) | Q(domain__name__icontains = search) | Q(domain__related_domain__name__icontains = search)
|
||||
if request.user.has_perms(('cableur',)):
|
||||
data = Interface.objects.filter(query_machine_list).distinct()
|
||||
else:
|
||||
data = Interface.objects.filter(query_machine_list & Q(machine__user__id = request.user.id)).distinct()
|
||||
for d in data:
|
||||
recherche['machines_list'].append(d.machine)
|
||||
if i == '2':
|
||||
recherche['facture_list'] = Facture.objects.filter(query & date_query).distinct()
|
||||
if i == '3':
|
||||
recherche['ban_list'] = Ban.objects.filter(query).distinct()
|
||||
if i == '4':
|
||||
recherche['white_list'] = Whitelist.objects.filter(query).distinct()
|
||||
if i == '5':
|
||||
recherche['port_list'] = Port.objects.filter(details__icontains = search).distinct()
|
||||
if not request.user.has_perms(('cableur',)):
|
||||
recherche['port_list'] = None
|
||||
if i == '6':
|
||||
recherche['switch_list'] = Switch.objects.filter(details__icontains = search).distinct()
|
||||
if not request.user.has_perms(('cableur',)):
|
||||
recherche['switch_list'] = None
|
||||
options, created = GeneralOption.objects.get_or_create()
|
||||
search_display_page = options.search_display_page
|
||||
def finish_results(results, col, order):
|
||||
"""Sort the results by applying filters and then limit them to the
|
||||
number of max results. Finally add the info of the nmax number of results
|
||||
to the dict"""
|
||||
|
||||
for r in recherche:
|
||||
if recherche[r] != None:
|
||||
recherche[r] = recherche[r][:search_display_page]
|
||||
results['users'] = SortTable.sort(
|
||||
results['users'],
|
||||
col,
|
||||
order,
|
||||
SortTable.USERS_INDEX
|
||||
)
|
||||
results['machines'] = SortTable.sort(
|
||||
results['machines'],
|
||||
col,
|
||||
order,
|
||||
SortTable.MACHINES_INDEX
|
||||
)
|
||||
results['factures'] = SortTable.sort(
|
||||
results['factures'],
|
||||
col,
|
||||
order,
|
||||
SortTable.COTISATIONS_INDEX
|
||||
)
|
||||
results['bans'] = SortTable.sort(
|
||||
results['bans'],
|
||||
col,
|
||||
order,
|
||||
SortTable.USERS_INDEX_BAN
|
||||
)
|
||||
results['whitelists'] = SortTable.sort(
|
||||
results['whitelists'],
|
||||
col,
|
||||
order,
|
||||
SortTable.USERS_INDEX_WHITE
|
||||
)
|
||||
results['rooms'] = SortTable.sort(
|
||||
results['rooms'],
|
||||
col,
|
||||
order,
|
||||
SortTable.TOPOLOGIE_INDEX_ROOM
|
||||
)
|
||||
results['ports'] = SortTable.sort(
|
||||
results['ports'],
|
||||
col,
|
||||
order,
|
||||
SortTable.TOPOLOGIE_INDEX_PORT
|
||||
)
|
||||
results['switches'] = SortTable.sort(
|
||||
results['switches'],
|
||||
col,
|
||||
order,
|
||||
SortTable.TOPOLOGIE_INDEX
|
||||
)
|
||||
|
||||
recherche.update({'max_result': search_display_page})
|
||||
options, _ = GeneralOption.objects.get_or_create()
|
||||
max_result = options.search_display_page
|
||||
for name, val in results.items():
|
||||
results[name] = val.distinct()[:max_result]
|
||||
results.update({'max_result': max_result})
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def search_single_word(word, filters, is_cableur, user_id,
|
||||
start, end, user_state, aff):
|
||||
""" Construct the correct filters to match differents fields of some models
|
||||
with the given query according to the given filters.
|
||||
The match field are either CharField or IntegerField that will be displayed
|
||||
on the results page (else, one might not see why a result has matched the
|
||||
query). IntegerField are matched against the query only if it can be casted
|
||||
to an int."""
|
||||
|
||||
# Users
|
||||
if '0' in aff:
|
||||
filter_users = (
|
||||
Q(
|
||||
surname__icontains=word
|
||||
) | Q(
|
||||
adherent__name__icontains=word
|
||||
) | Q(
|
||||
pseudo__icontains=word
|
||||
) | Q(
|
||||
club__room__name__icontains=word
|
||||
) | Q(
|
||||
adherent__room__name__icontains=word
|
||||
)
|
||||
) & Q(state__in=user_state)
|
||||
if not is_cableur:
|
||||
filter_users &= Q(id=user_id)
|
||||
filters['users'] |= filter_users
|
||||
|
||||
# Machines
|
||||
if '1' in aff:
|
||||
filter_machines = Q(
|
||||
name__icontains=word
|
||||
) | (
|
||||
Q(
|
||||
user__pseudo__icontains=word
|
||||
) & Q(
|
||||
user__state__in=user_state
|
||||
)
|
||||
) | Q(
|
||||
interface__domain__name__icontains=word
|
||||
) | Q(
|
||||
interface__domain__related_domain__name__icontains=word
|
||||
) | Q(
|
||||
interface__mac_address__icontains=word
|
||||
) | Q(
|
||||
interface__ipv4__ipv4__icontains=word
|
||||
)
|
||||
if not is_cableur:
|
||||
filter_machines &= Q(user__id=user_id)
|
||||
filters['machines'] |= filter_machines
|
||||
|
||||
# Factures
|
||||
if '2' in aff:
|
||||
filter_factures = Q(
|
||||
user__pseudo__icontains=word
|
||||
) & Q(
|
||||
user__state__in=user_state
|
||||
)
|
||||
if start is not None:
|
||||
filter_factures &= Q(date__gte=start)
|
||||
if end is not None:
|
||||
filter_factures &= Q(date__lte=end)
|
||||
filters['factures'] |= filter_factures
|
||||
|
||||
# Bans
|
||||
if '3' in aff:
|
||||
filter_bans = (
|
||||
Q(
|
||||
user__pseudo__icontains=word
|
||||
) & Q(
|
||||
user__state__in=user_state
|
||||
)
|
||||
) | Q(
|
||||
raison__icontains=word
|
||||
)
|
||||
if start is not None:
|
||||
filter_bans &= (
|
||||
Q(date_start__gte=start) & Q(date_end__gte=start)
|
||||
) | (
|
||||
Q(date_start__lte=start) & Q(date_end__gte=start)
|
||||
) | (
|
||||
Q(date_start__gte=start) & Q(date_end__lte=start)
|
||||
)
|
||||
if end is not None:
|
||||
filter_bans &= (
|
||||
Q(date_start__lte=end) & Q(date_end__lte=end)
|
||||
) | (
|
||||
Q(date_start__lte=end) & Q(date_end__gte=end)
|
||||
) | (
|
||||
Q(date_start__gte=end) & Q(date_end__lte=end)
|
||||
)
|
||||
filters['bans'] |= filter_bans
|
||||
|
||||
# Whitelists
|
||||
if '4' in aff:
|
||||
filter_whitelists = (
|
||||
Q(
|
||||
user__pseudo__icontains=word
|
||||
) & Q(
|
||||
user__state__in=user_state
|
||||
)
|
||||
) | Q(
|
||||
raison__icontains=word
|
||||
)
|
||||
if start is not None:
|
||||
filter_whitelists &= (
|
||||
Q(date_start__gte=start) & Q(date_end__gte=start)
|
||||
) | (
|
||||
Q(date_start__lte=start) & Q(date_end__gte=start)
|
||||
) | (
|
||||
Q(date_start__gte=start) & Q(date_end__lte=start)
|
||||
)
|
||||
if end is not None:
|
||||
filter_whitelists &= (
|
||||
Q(date_start__lte=end) & Q(date_end__lte=end)
|
||||
) | (
|
||||
Q(date_start__lte=end) & Q(date_end__gte=end)
|
||||
) | (
|
||||
Q(date_start__gte=end) & Q(date_end__lte=end)
|
||||
)
|
||||
filters['whitelists'] |= filter_whitelists
|
||||
|
||||
# Rooms
|
||||
if '5' in aff and is_cableur:
|
||||
filter_rooms = Q(
|
||||
details__icontains=word
|
||||
) | Q(
|
||||
name__icontains=word
|
||||
) | Q(
|
||||
port__details=word
|
||||
)
|
||||
filters['rooms'] |= filter_rooms
|
||||
|
||||
# Switch ports
|
||||
if '6' in aff and is_cableur:
|
||||
filter_ports = Q(
|
||||
room__name__icontains=word
|
||||
) | Q(
|
||||
machine_interface__domain__name__icontains=word
|
||||
) | Q(
|
||||
related__switch__switch_interface__domain__name__icontains=word
|
||||
) | Q(
|
||||
radius__icontains=word
|
||||
) | Q(
|
||||
vlan_force__name__icontains=word
|
||||
) | Q(
|
||||
details__icontains=word
|
||||
)
|
||||
if is_int(word):
|
||||
filter_ports |= Q(
|
||||
port=word
|
||||
)
|
||||
filters['ports'] |= filter_ports
|
||||
|
||||
# Switches
|
||||
if '7' in aff and is_cableur:
|
||||
filter_switches = Q(
|
||||
switch_interface__domain__name__icontains=word
|
||||
) | Q(
|
||||
switch_interface__ipv4__ipv4__icontains=word
|
||||
) | Q(
|
||||
location__icontains=word
|
||||
) | Q(
|
||||
stack__name__icontains=word
|
||||
) | Q(
|
||||
model__reference__icontains=word
|
||||
) | Q(
|
||||
model__constructor__name__icontains=word
|
||||
) | Q(
|
||||
details__icontains=word
|
||||
)
|
||||
if is_int(word):
|
||||
filter_switches |= Q(
|
||||
number=word
|
||||
) | Q(
|
||||
stack_member_id=word
|
||||
)
|
||||
filters['switches'] |= filter_switches
|
||||
|
||||
return filters
|
||||
|
||||
|
||||
def get_words(query):
|
||||
"""Function used to split the uery in different words to look for.
|
||||
The rules are simple :
|
||||
- anti-slash ('\\') is used to escape characters
|
||||
- anything between quotation marks ('"') is kept intact (not
|
||||
interpreted as separators) excepts anti-slashes used to escape
|
||||
- spaces (' ') and commas (',') are used to separated words
|
||||
"""
|
||||
|
||||
words = []
|
||||
i = 0
|
||||
keep_intact = False
|
||||
escaping_char = False
|
||||
for char in query:
|
||||
if i >= len(words):
|
||||
# We are starting a new word
|
||||
words.append('')
|
||||
if escaping_char:
|
||||
# The last char war a \ so we escape this char
|
||||
escaping_char = False
|
||||
words[i] += char
|
||||
continue
|
||||
if char == '\\':
|
||||
# We need to escape the next char
|
||||
escaping_char = True
|
||||
continue
|
||||
if char == '"':
|
||||
# Toogle the keep_intact state, if true, we are between two "
|
||||
keep_intact = not keep_intact
|
||||
continue
|
||||
if keep_intact:
|
||||
# If we are between two ", ignore separators
|
||||
words[i] += char
|
||||
continue
|
||||
if char == ' ' or char == ',':
|
||||
# If we encouter a separator outside of ", we create a new word
|
||||
if words[i] is not '':
|
||||
i += 1
|
||||
continue
|
||||
# If we haven't encountered any special case, add the char to the word
|
||||
words[i] += char
|
||||
|
||||
return words
|
||||
|
||||
|
||||
def get_results(query, request, params):
|
||||
"""The main function of the search procedure. It gather the filters for
|
||||
each of the different words of the query and concatenate them into a
|
||||
single filter. Then it calls 'finish_results' and return the queryset of
|
||||
objects to display as results"""
|
||||
|
||||
start = params.get('s', None)
|
||||
end = params.get('e', None)
|
||||
user_state = params.get('u', initial_choices(CHOICES_USER))
|
||||
aff = params.get('a', initial_choices(CHOICES_AFF))
|
||||
|
||||
filters = {
|
||||
'users': Q(),
|
||||
'machines': Q(),
|
||||
'factures': Q(),
|
||||
'bans': Q(),
|
||||
'whitelists': Q(),
|
||||
'rooms': Q(),
|
||||
'ports': Q(),
|
||||
'switches': Q()
|
||||
}
|
||||
|
||||
words = get_words(query)
|
||||
for word in words:
|
||||
filters = search_single_word(
|
||||
word,
|
||||
filters,
|
||||
request.user.has_perms(('cableur',)),
|
||||
request.user.id,
|
||||
start,
|
||||
end,
|
||||
user_state,
|
||||
aff
|
||||
)
|
||||
|
||||
results = {
|
||||
'users': User.objects.filter(filters['users']),
|
||||
'machines': Machine.objects.filter(filters['machines']),
|
||||
'factures': Facture.objects.filter(filters['factures']),
|
||||
'bans': Ban.objects.filter(filters['bans']),
|
||||
'whitelists': Whitelist.objects.filter(filters['whitelists']),
|
||||
'rooms': Room.objects.filter(filters['rooms']),
|
||||
'ports': Port.objects.filter(filters['ports']),
|
||||
'switches': Switch.objects.filter(filters['switches'])
|
||||
}
|
||||
|
||||
results = finish_results(
|
||||
results,
|
||||
request.GET.get('col'),
|
||||
request.GET.get('order')
|
||||
)
|
||||
results.update({'search_term': query})
|
||||
|
||||
return results
|
||||
|
||||
return recherche
|
||||
|
||||
@login_required
|
||||
def search(request):
|
||||
search = SearchForm(request.POST or None)
|
||||
if search.is_valid():
|
||||
return form(search_result(search, False, request), 'search/index.html',request)
|
||||
return form({'searchform' : search}, 'search/search.html', request)
|
||||
""" La page de recherche standard """
|
||||
search_form = SearchForm(request.GET or None)
|
||||
if search_form.is_valid():
|
||||
return render(
|
||||
request,
|
||||
'search/index.html',
|
||||
get_results(
|
||||
search_form.cleaned_data.get('q', ''),
|
||||
request,
|
||||
search_form.cleaned_data
|
||||
)
|
||||
)
|
||||
return render(request, 'search/search.html', {'search_form': search_form})
|
||||
|
||||
|
||||
@login_required
|
||||
def searchp(request):
|
||||
search = SearchFormPlus(request.POST or None)
|
||||
if search.is_valid():
|
||||
return form(search_result(search, True, request), 'search/index.html',request)
|
||||
return form({'searchform' : search}, 'search/search.html', request)
|
||||
""" La page de recherche avancée """
|
||||
search_form = SearchFormPlus(request.GET or None)
|
||||
if search_form.is_valid():
|
||||
return render(
|
||||
request,
|
||||
'search/index.html',
|
||||
get_results(
|
||||
search_form.cleaned_data.get('q', ''),
|
||||
request,
|
||||
search_form.cleaned_data
|
||||
)
|
||||
)
|
||||
return render(request, 'search/search.html', {'search_form': search_form})
|
||||
|
|
|
@ -73,10 +73,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% endif %}
|
||||
</ul>
|
||||
<div class="col-sm-3 col-md-3 navbar-right">
|
||||
<form action="{% url "search:search"%}" method="POST" class="navbar-form" role="search">
|
||||
{% csrf_token %}
|
||||
<form action="{% url "search:search"%}" class="navbar-form" role="search">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" placeholder="Search" name="search_field" id="search-term">
|
||||
<input type="text" class="form-control" placeholder="Search" name="q" id="search-term" {% if search_term %}value="{{ search_term }}"{% endif %}>
|
||||
<div class="input-group-btn">
|
||||
<button class="btn btn-default" type="submit"><i class="glyphicon glyphicon-search"></i></button>
|
||||
<a href="{% url "search:searchp" %}" class="btn btn-default" role="button"><i class="glyphicon glyphicon-plus"></i></a>
|
||||
|
@ -199,7 +198,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
<footer class="navbar">
|
||||
<div class="containerfluid text-center">
|
||||
<p>Re2o 2016 - Gabriel Détraz, <a href="https://gitlab.rezometz.org/lhark">Goulven Kermarec</a>, Augustin Lemesle</p>
|
||||
<p>Re2o 2016 - Gabriel Détraz, <a href="https://gitlab.rezometz.org/lhark">Goulven Kermarec</a>, Augustin Lemesle, Maël Kervella</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
|
40
templates/buttons/multiple_checkbox_alt.html
Normal file
40
templates/buttons/multiple_checkbox_alt.html
Normal file
|
@ -0,0 +1,40 @@
|
|||
{% 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 %}
|
||||
|
||||
<div class="form-group {% if field.form.errors %}{% if field.errors %}has-error{% else %}has-success{% endif %}{% endif %}">
|
||||
<label class="control-label" for="{{ field.id_for_label }}">
|
||||
{{ field.label }}
|
||||
</label>
|
||||
<div id="{{ field.auto_id }}" data-toggle="buttons">
|
||||
{% for val in field.field.choices %}
|
||||
<label for="id_u_{{ val.0 }}" class="btn btn-default{% if val.0 in field.initial %} active{% endif %}">
|
||||
<input {% if val.0 in field.initial %}checked="checked" {% endif %}class="" id="id_u_{{ val.0 }}" name="{{ field.name }}" title="" type="checkbox" value="{{ val.0 }}" /> {{ val.1 }}
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% for error in field.errors %}
|
||||
<div class="help-block">{{ error }}</div>
|
||||
{% endfor %}
|
||||
<div class="help-block">{{ field.help_text }}</div>
|
||||
</div>
|
Loading…
Reference in a new issue