diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 00000000..95aaebe3
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -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
+
+
+
+
diff --git a/README.md b/README.md
index 023d186a..fb234ea1 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/cotisations/forms.py b/cotisations/forms.py
index 354da1f1..7725016c 100644
--- a/cotisations/forms.py
+++ b/cotisations/forms.py
@@ -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
diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py
index 16f3f084..88bee71e 100644
--- a/freeradius_utils/auth.py
+++ b/freeradius_utils/auth.py
@@ -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:
diff --git a/re2o/templatetags/url_insert_param.py b/re2o/templatetags/url_insert_param.py
index 7e37a71b..90f7a0a6 100644
--- a/re2o/templatetags/url_insert_param.py
+++ b/re2o/templatetags/url_insert_param.py
@@ -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]
diff --git a/re2o/utils.py b/re2o/utils.py
index 0fa6a84c..a6e5c851 100644
--- a/re2o/utils.py
+++ b/re2o/utils.py
@@ -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
diff --git a/search/admin.py b/search/admin.py
index bcdc4f1d..decd096a 100644
--- a/search/admin.py
+++ b/search/admin.py
@@ -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.
diff --git a/search/forms.py b/search/forms.py
index fa43be55..b0668ef9 100644
--- a/search/forms.py
+++ b/search/forms.py
@@ -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"
+ )
diff --git a/search/models.py b/search/models.py
deleted file mode 100644
index 8d1fa0e4..00000000
--- a/search/models.py
+++ /dev/null
@@ -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")
diff --git a/search/templates/search/index.html b/search/templates/search/index.html
index 859193fb..98658bfb 100644
--- a/search/templates/search/index.html
+++ b/search/templates/search/index.html
@@ -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 %}
Résultats dans les utilisateurs
- {% 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 %}
Résultats dans les machines :
- {% 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 %}
Résultats dans les factures :
- {% 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 %}
Résultats dans les accès à titre gracieux :
- {% 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 %}
Résultats dans les banissements :
- {% include "users/aff_bans.html" with ban_list=ban_list %}
+ {% include "users/aff_bans.html" with ban_list=bans %}
{% endif %}
- {% if switch_list %}
- Résultats dans les switchs :
- {% include "topologie/aff_switch.html" with switch_list=switch_list %}
+ {% if rooms %}
+ Résultats dans les chambres :
+ {% include "topologie/aff_chambres.html" with room_list=rooms %}
{% endif %}
- {% if port_list %}
+ {% if ports %}
Résultats dans les ports :
- {% 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 %}
+ Résultats dans les switchs :
+ {% 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 %}
Aucun résultat
+ {% else %}
+ (Seulement les {{ max_result }} premiers résultats sont affichés dans chaque catégorie)
{% endif %}
- (Seulement les {{ max_result }} premiers résultats sont affichés dans chaque catégorie)
diff --git a/search/templates/search/search.html b/search/templates/search/search.html
index adb5dd92..7ae5d56d 100644
--- a/search/templates/search/search.html
+++ b/search/templates/search/search.html
@@ -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 %}
-
diff --git a/search/urls.py b/search/urls.py
index 3b16fcd1..dc1490e5 100644
--- a/search/urls.py
+++ b/search/urls.py
@@ -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'),
]
diff --git a/search/views.py b/search/views.py
index 16c365d8..a561dd11 100644
--- a/search/views.py
+++ b/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})
diff --git a/templates/base.html b/templates/base.html
index d0d5e99a..7a886abc 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -73,10 +73,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endif %}